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.
Related
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.
I am making an app that translates a word from one language to English and gets information about it (e.g. definition, use in a sentence, synonyms, sound representation)
What my function does:
Searches for the translation in the database. If it is found, we return it.
If it is not found we translate a word using google translate, or Yandex translate API.
If translation is found we download it's sound representation, save the translation to the database and add additional information from other API's
We return a json response with all of the information.
Now my controllers method is really big and I can't find a cleaner way to go about it.
Any help is appreciated.
public function store(Request $request)
{
$translated = $request->get('translated');
$translation = $this->translation->findBy('translated', $translated)->first();
if ($translation) {
return Response::json(['translation' => $this->translation->with(['examples', 'definitions', 'synonyms', 'images'])->find($translation->id)], ResponseCode::HTTP_CREATED);
}
$data = $request->all();
$data['translation'] = $this->translate($translated);
if ($translated == $data['translation']) {
Log::info('Translation not found: ' . $data['translation']);
return $this->translationNotFound();
}
$downloader = new Downloader(new GoogleSpeechDownloader());
$filename = $downloader->download($data['translation']);
if ($filename) $data['sound_name'] = $filename;
$translation = $this->translation->create($data);
$this->createDefinition($translation);
$this->createExample($translation);
$this->createSynonym($translation);
return Response::json(['translation' => $this->translation->with(['examples', 'definitions', 'synonyms', 'images'])->find($translation->id)], ResponseCode::HTTP_CREATED);
}
private function translationNotFound()
{
return Response::json(['error' => 'Vertimas nerastas.'], ResponseCode::HTTP_NOT_FOUND);
}
private function createDefinition($translation)
{
$definition = new Definition();
$definer = new Definer(new DictionaryApiDefiner());
try {
$definition->definition = $definer->getDefinition($translation->translation);
$definition->approved = true;
$translation->definitions()->save($definition);
} catch (\Exception $e) {
Log::alert('Definition for word ' . $translation->translation . ' not found.');
}
}
private function createExample($translation)
{
$example = new Example();
$exampler = new ExampleCreator(new YourDictionaryGouteParserExampler());
try {
$example->example = $exampler->getExample($translation->translation);
$example->approved = true;
$translation->examples()->save($example);
} catch (\Exception $e) {
Log::alert('Example for word ' . $translation->translation . ' not found.');
}
}
private function createSynonym($translation)
{
$creator = new SynonymCreator(new BigHugeLabsSynonymCreator());
foreach ($creator->getSynonyms($translation->translation) as $s) {
$synonym = new Synonym();
$synonym->synonym = $s;
$synonym->approved = true;
$translation->synonyms()->save($synonym);
}
}
private function translate($translated)
{
$translator = new Translator(new GoogleTranslator());
try {
return $translator->translate($translated);
} catch (\Exception $e) {
Log::critical($e->getMessage());
}
$translator = new Translator(new YandexTranslator());
return $translator->translate($translated);
}
If you want cleaner code, just make a class for this job. Two classes for this two API's and in the controller make the check for the word, if not exist in the database, check in the other two API's, just split every action to method in the new two classes that you will make.
I am attempting to add logging for the envelope generated by a third party library. I am modifying the updateMetadataField() method below.
I am creating $client like so:
$client = new UpdateClient($UPDATE_END_POINT, $USER_AUTH_ARRAY);
I have tried both $this->client->__getLastRequest() and $this->__getLastRequest() with the same error as a result.
When the SoapClient is instantiated trace is set to true.
Error is
Fatal error: Call to undefined method UpdateClient::__getLastRequest()
So how do I correctly access the __getLastRequest() method?
$USER_AUTH_ARRAY = array(
'login'=>"foo",
'password'=>"bar",
'exceptions'=>0,
'trace'=>true,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS
);
class UpdateClient {
private $client;
public function __construct($endpoint, $auth_array) {
$this->client = new SoapClient($endpoint, $auth_array);
}
public function updateMetadataField($uuid, $key, $value) {
$result = $this->client->updateMetadataField(array(
'assetUuid' => $uuid,
'key' => $key,
'value' => $value)
);
if(is_soap_fault($result)) {
return $result;
}
return $result->return . "\n\n" . $this->client->__getLastRequest();
} // updateMetadataField()
} // UpdateClient
UPDATE - adding calling code This code iterates over an array which maps our data to the remote fields.
What I am hoping to do is begin storing the envelope we send to aid in debugging.
$client = new UpdateClient($UPDATE_END_POINT, $USER_AUTH_ARRAY);
foreach ($widen_to_nool_meta_map as $widen => $nool) { // array defined in widen.php
if ($nool != '') {
// handle exceptions
if ($nool == 'asset_created') { // validate as date - note that Widen pulls exif data so we don't need to pass this
if (!strtotime($sa->$nool)) {
continue;
}
} else if ($nool == 'people_in_photo' || $nool == 'allow_sublicensing' || $nool == 'allowed_use_pr_gallery') {
// we store as 0/1 but GUI at Widen wants Yes/No
$sa->$nool = ($sa->$nool == '1') ? 'Yes' : 'No';
} else if ($nool == 'credit_requirements') {
$sa->$nool = $sa->credit_requirements()->label;
}
$result = $client->updateMetadataField($sa->widen_id, $widen, $sa->$nool);
if(is_soap_fault($result)) {
$sync_result = $sync_result . "\n" . $result->getMessage();
} else {
$sync_result = $sync_result . "\n" . print_r($result, 1);
}
} // nool field set
} // foreach mapped field
If you want to access UpdateClient::__getLastRequest() you have to expose that method on the UpdateClient class since the $client is a private variable. The correct way of calling it is $this->client->__getLastRequest().
Take a look at this working example, as you can see I'm consuming a free web service for testing purposes.
<?php
$USER_AUTH_ARRAY = array(
'exceptions'=>0,
'trace'=>true,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS
);
class TestClient {
private $client;
public function __construct($endpoint, $auth_array) {
$this->client = new SoapClient($endpoint, $auth_array);
}
public function CelsiusToFahrenheit( $celsius ) {
$result = $this->client->CelsiusToFahrenheit(array(
'Celsius' => $celsius
)
);
if(is_soap_fault($result)) {
return $result;
}
return $result;
}
public function __getLastRequest() {
return $this->client->__getLastRequest();
}
}
try
{
$test = new TestClient( "http://www.w3schools.com/webservices/tempconvert.asmx?wsdl", $USER_AUTH_ARRAY);
echo "<pre>";
var_dump($test->CelsiusToFahrenheit( 0 ));
var_dump($test->__getLastRequest());
var_dump($test->CelsiusToFahrenheit( 20 ));
var_dump($test->__getLastRequest());
echo "</pre>";
}
catch (SoapFault $fault)
{
echo $fault->faultcode;
}
?>
I am using Phalcon. The following function is called when accessing route: /registration/facebook/access:
public function facebookAccess()
{
$app = new FacebookApp($this->config);
$app->initialize();
$loginUrl = $app->getRegistrationLink($this->fbRedirectUrl);
$this->view->setVar('loginUrl', $loginUrl);
}
$app->initialize() calls session_start() and FacebookSession::setDefaultApplication with my app id and secret. The link appears correctly. When I click on the link, I get redirected to /registration/facebook/process:
public function facebookProcess()
{
$app = new FacebookApp($this->config);
$app->initialize();
if (!$app->startSession($this->fbRedirectUrl))
{
if ($app->getSessionError())
$this->dispatchError($app->getSessionError());
else
$this->dispatchErrorStub('FACEBOOK_SESSION_INVALID');
return;
}
$fbData = $app->getUser();
if (!$fbData->save())
{
$this->dispatchErrorOn($fbData);
return;
}
$this->view->setVar('fbData', $fbData);
}
where startSession is defined as follows:
public function startSession($redirectUrl)
{
$helper = new FacebookRedirectLoginHelper($redirectUrl);
try
{
$this->session = $helper->getSessionFromRedirect();
$this->sessionError = false;
if (!$this->session)
return false;
return true;
}
catch(Exception $ex)
{
$this->session = false;
$this->sessionError = $ex->getMessage();
return false;
}
}
But the returned session is always true. The code worked fine yesterday, I just did some refactoring. Cannot figure out the problem.
It resolved itself without doing nothing... still I don't know what went wrong.
I am using joomla 2.5, I am working on custom component of joomla . I have created the form in backend admin page. what i need is , i want to save the post data of form in params row of that component in database of #_extensions.
Here is my tables/component.php
<?php defined('_JEXEC') or die('Restricted access');
jimport('joomla.database.table');
class componentTablecomponent extends JTable {
function __construct(&$db)
{
parent::__construct('#__extensions', 'extension_id', $db);
}
public function bind($array, $ignore = '')
{
if (isset($array['params']) && is_array($array['params']))
{
// Convert the params field to a string.
$parameter = new JRegistry;
$parameter->loadArray($array['params']);
$array['params'] = (string)$parameter;
}
return parent::bind($array, $ignore);
}
public function load($pk = null, $reset = true)
{
if (parent::load($pk, $reset))
{
// Convert the params field to a registry.
$params = new JRegistry;
$params->loadJSON($this->params);
$this->params = $params;
return true;
}
else
{
return false;
}
} public function store() {
parent::store(null);
}
}
Instead of saving the $data in params row of that component . This code is creating new empty rows in that table(data is saving in those params field).
Here is my save() function in controllers/component.php
public function save()
{
// Check for request forgeries.
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
// Check if the user is authorized to do this.
if (!JFactory::getUser()->authorise('core.admin'))
{
`JFactory::getApplication()->redirect('index.php',JText::_('JERROR_ALERTNOAUTHOR'));`
return;
}
// Initialise variables.
$app = JFactory::getApplication();
$model = $this->getModel('component');
$form = $model->getForm();
$data = JRequest::getVar('jform', array(), 'post', 'array');
print_r($data);
// Validate the posted data.
$return = $model->validate($form, $data);
// Check for validation errors.
if ($return === false)
{
// Get the validation messages.
$errors = $model->getErrors();
// Push up to three validation messages out to the user.
for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) {
if ($errors[$i] instanceof Exception) {
$app->enqueueMessage($errors[$i]->getMessage(), 'warning');
} else {
$app->enqueueMessage($errors[$i], 'warning');
}
}
// Redirect back to the edit screen.
$this->setRedirect(JRoute::_('somelink',false));
return false;
}
// Attempt to save the configuration.
$data = $return;
$return = $model->save($data);
// Check the return value.
if ($return === false)
{
// Save failed, go back to the screen and display a notice.
$message = JText::sprintf('JERROR_SAVE_FAILED', $model->getError());
$this->setRedirect('index.php/somelink','error');
return false;
}
// Set the success message.
$message = JText::_('COM_CONFIG_SAVE_SUCCESS');
// Set the redirect based on the task.
switch ($this->getTask())
{
case 'apply':
$this->setRedirect('somelink',$message);
break;
case 'cancel':
case 'save':
default:
$this->setRedirect('index.php', $message);
break;
}
return true;
}
models/component.php
public function save($data) {
parent::save($data);
return true;
}
I thinks these codes are enough . I can add more codes if you need.
If you want to store params in the extension table, why not just use com_config for it?
See http://docs.joomla.org/J2.5:Developing_a_MVC_Component/Adding_configuration
Then you don't have to do anything besides creating the config.xml and adding a link to the config options in the toolbar.
I have come up with a solution . it works for me . By using this method you can control save () functions i.e. you can run your custom php scripts on save, apply toolbar buttons.
I copied save(),save($data), store($updateNulls = false) functions from com_config component , I have copied the layout from component view . Removed the buttons. Added toolbar buttons in .html.php file.
controllers/mycomponent.php
<?php
defined('_JEXEC') or die;
class componentControllermycomponent extends JControllerLegacy {
function __construct($config = array())
{
parent::__construct($config);
// Map the apply task to the save method.
$this->registerTask('apply', 'save');
}
function save()
{
// Check for request forgeries.
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
// Set FTP credentials, if given.
JClientHelper::setCredentialsFromRequest('ftp');
// Initialise variables.
$app = JFactory::getApplication();
$model = $this->getModel('mymodel');
$form = $model->getForm();
$data = JRequest::getVar('jform', array(), 'post', 'array');
$id = JRequest::getInt('id');
$option = 'com_mycomponent';
// Check if the user is authorized to do this.
if (!JFactory::getUser()->authorise('core.admin', $option))
{
JFactory::getApplication()->redirect('index.php', JText::_('JERROR_ALERTNOAUTHOR'));
return;
}
// Validate the posted data.
$return = $model->validate($form, $data);
// Check for validation errors.
if ($return === false) {
// Get the validation messages.
$errors = $model->getErrors();
// Push up to three validation messages out to the user.
for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) {
if ($errors[$i] instanceof Exception) {
$app->enqueueMessage($errors[$i]->getMessage(), 'warning');
} else {
$app->enqueueMessage($errors[$i], 'warning');
}
}
// Save the data in the session.
$app->setUserState('com_iflychat.config.global.data', $data);
// Redirect back to the edit screen.
$this->setRedirect(JRoute::_('index.php?option=com_mycomponent&view=component&component='.$option.'&tmpl=component', false));
return false;
}
// Attempt to save the configuration.
$data = array(
'params' => $return,
'id' => $id,
'option' => $option
);
// print_r($data);
$return = $model->save($data);
// Check the return value.
if ($return === false)
{
// Save the data in the session.
$app->setUserState('com_config.config.global.data', $data);
// Save failed, go back to the screen and display a notice.
$message = JText::sprintf('JERROR_SAVE_FAILED', $model->getError());
$this->setRedirect('index.php?option=com_mycomponent&view=component&component='.$option.'&tmpl=component', $message, 'error');
return false;
}
// Set the redirect based on the task.
switch ($this->getTask())
{
case 'apply':
$message = JText::_('COM_MYCOMPONENT_SAVE_SUCCESS');
print_r($data);
$this->setRedirect('index.php?option=com_mycomponent&view=myview&layout=edit', $message);
break;
case 'save':
default:
$this->setRedirect('index.php');
break;
}
return true;
}
function cancel()
{
$this->setRedirect('index.php');
}
}
models/mymodel.php
<?php
defined('_JEXEC') or die;
jimport('joomla.application.component.modelform');
class componentModelmymodel extends JModelForm {
protected $event_before_save = 'onConfigurationBeforeSave';
protected $event_after_save = 'onConfigurationAfterSave';
public function getForm($data = array(), $loadData = true){
// Get the form.
$form = $this->loadForm('com_mycomponent.form', 'config',
array('control' => 'jform', 'load_data' => $loadData));
if (empty($form)){
return false;
}
return $form;
}
public function save($data)
{
$dispatcher = JDispatcher::getInstance();
$table = JTable::getInstance('extension');
$isNew = true;
// Save the rules.
if (isset($data['params']) && isset($data['params']['rules']))
{
$rules = new JAccessRules($data['params']['rules']);
$asset = JTable::getInstance('asset');
if (!$asset->loadByName($data['option']))
{
$root = JTable::getInstance('asset');
$root->loadByName('root.1');
$asset->name = $data['option'];
$asset->title = $data['option'];
$asset->setLocation($root->id, 'last-child');
}
$asset->rules = (string) $rules;
if (!$asset->check() || !$asset->store())
{
$this->setError($asset->getError());
return false;
}
// We don't need this anymore
unset($data['option']);
unset($data['params']['rules']);
}
// Load the previous Data
if (!$table->load($data['id']))
{
$this->setError($table->getError());
return false;
}
unset($data['id']);
// Bind the data.
if (!$table->bind($data))
{
$this->setError($table->getError());
return false;
}
// Check the data.
if (!$table->check())
{
$this->setError($table->getError());
return false;
}
// Trigger the oonConfigurationBeforeSave event.
$result = $dispatcher->trigger($this->event_before_save, array($this->option . '.' . $this->name, $table, $isNew));
if (in_array(false, $result, true))
{
$this->setError($table->getError());
return false;
}
// Store the data.
if (!$table->store())
{
$this->setError($table->getError());
return false;
}
// Clean the component cache.
$this->cleanCache('_system');
// Trigger the onConfigurationAfterSave event.
$dispatcher->trigger($this->event_after_save, array($this->option . '.' . $this->name, $table, $isNew));
return true;
}
function getComponent()
{
$result = JComponentHelper::getComponent('com_mycomponent');
return $result;
}
}
tables/mycomponent.php
<?php
// No direct access
defined('_JEXEC') or die('Restricted access');
// import Joomla table library
jimport('joomla.database.table');
/**
* Hello Table class
*/
class componentTableMycomponent extends JTable
{
function __construct(&$db)
{
parent::__construct('#__extensions', 'extension_id', $db);
}
public function bind($array, $ignore = '')
{
if (isset($array['params']) && is_array($array['params']))
{
// Convert the params field to a string.
$parameter = new JRegistry;
$parameter->loadArray($array['params']);
$array['params'] = (string)$parameter;
}
return parent::bind($array, $ignore);
}
public function load($pk = null, $reset = true)
{
if (parent::load($pk, $reset))
{
// Convert the params field to a registry.
$params = new JRegistry;
$params->loadJSON($this->params);
$this->params = $params;
return true;
}
else
{
return false;
}
}
public function store($updateNulls = false)
{
// Transform the params field
if (is_array($this->params)) {
$registry = new JRegistry();
$registry->loadArray($this->params);
$this->params = (string)$registry;
}
$date = JFactory::getDate();
$user = JFactory::getUser();
if ($this->id) {
// Existing item
$this->modified = $date->toSql();
$this->modified_by = $user->get('id');
} else {
// New newsfeed. A feed created and created_by field can be set by the user,
// so we don't touch either of these if they are set.
if (!intval($this->created)) {
$this->created = $date->toSql();
}
if (empty($this->created_by)) {
$this->created_by = $user->get('id');
}
}
// Verify that the alias is unique
$table = JTable::getInstance('Yourinstance', 'mycomponentTable');
if ($table->load(array('alias'=>$this->alias, 'catid'=>$this->catid)) && ($table->id != $this->id || $this->id==0)) {
$this->setError(JText::_('COM_CONTACT_ERROR_UNIQUE_ALIAS'));
return false;
}
// Attempt to store the data.
return parent::store($updateNulls);
}
}
Note : your config.xml file should be in models/forms folder.