I am writing my own session handler as below to make Aerospike as the session manager. However, it is throwing warnings about session handler.
Warning: session_start(): Cannot find save handler '' - session startup failed in /var/www/session.php on line 165
I have set the values of session.save_path and session.save_handler to "" in php.ini as I want to manage the session data storage and retrieval myself with the below class.
NOTE: I can use the default aerospike session handling but it saves session data as bytes(hexadecimal format), I can't use it as it is as other applications need to read this data as well, so I am trying to save data as a json encoded string.
A weird behavior is that the close method always gets called at script shutdown even though the session_set_save_handler's return value is false!
This works sometimes without error and sometimes throws a warning shown above. Not sure what is missing.
Code:
<?php
ini_set( 'display_errors', 1 );
ini_set( 'error_reporting', E_ALL );
define('SESS_ID', '66ac548234f96b48b42e18b2d3d7b73a3f1aceb01fa4c20647d3dcaa055b5099');
class MySessionHandler implements SessionHandlerInterface {
private $database = null;
private $recordKey = null;
public function __construct(){
$this->init();
}
private function init() {
$this->database = new \Aerospike(
[
"hosts" => [
0 => [
"addr" => "IP_HERE",
"port" => 3000
]
]
], false);
$this->recordKey = $this->database->initKey( 'cache', 'data', SESS_ID);
}
private function isConnected() : bool {
return ( $this->database instanceof \Aerospike ) && $this->database->isConnected();
}
public function open($savepath = '', $id = ''){
// If successful
if( is_null($this->database) ) {
$this->init();
}
$status = $this->database->get($this->recordKey, $data);
if ($status == \Aerospike::OK) {
$data = json_decode($data['bins']['PHP_SESSION'], 1);
if( !is_array($data) ) {
$data = [];
}
} else {
$data = [];
}
return true;
}
public function read($id)
{
if( is_null($this->database) ) {
$this->init();
}
$status = $this->database->get($this->recordKey, $data);
if ($status == \Aerospike::OK) {
$data = json_decode($data['bins']['PHP_SESSION'], 1);
if( !is_array($data) ) {
$data = [];
}
} else {
$data = [];
}
$_SESSION = $data;
return json_encode($data);
}
public function write($id, $dataNode)
{
if( is_null($this->database) ) {
$this->init();
}
$data = false;
if( $this->isConnected() ) {
$bins = [
"PHP_SESSION" => json_encode($_SESSION)
];
$status = $this->database->put( $this->recordKey, $bins );
if ($status == \Aerospike::OK) {
$data = true;
} else {
// error while saving data, log it
}
}
return $data;
}
public function destroy($id)
{
$removeStatus = false;
if( $this->isConnected() ) {
$status = $this->database->remove( $this->recordKey );
if ($status == \Aerospike::OK) {
$removeStatus = true;
} else {
// error while saving data, log it
}
}
return $removeStatus;
}
public function close(){
// Close the database connection
if($this->isConnected() && $this->database->close()){
// Return True
return true;
}
// Return False
return false;
}
public function gc($max)
{
return 0;
}
public function __destruct()
{
$this->close();
}
}
$s = new MySessionHandler();
// Set handler to overide SESSION
$newSession = session_set_save_handler($s, true);
var_dump($newSession); // this returns false sometimes and throws a warning
register_shutdown_function('session_write_close');
session_id(SESS_ID);
session_start();
$_SESSION['dfc'] = 'xdc1';
//unset($_SESSION['dfc']);
pr($_SESSION);
unset($s);
function pr($data) {
if( is_object($data) ) {
// $data = serialize($data);
}
echo '<pre>' . var_export($data, 1) . '</pre>';
}
These warnings are thrown intermittently and I am not sure what might be causing this. Any help would be appreciated.
The PHP client for Aerospike comes with a session handler. Set session.save_handler=aerospike. See the php.ini options of the module.
If you're writing your own session handler
Don't set session.save_handler=''. You want to comment it out in your php.ini instead, because that gets loaded and executed first (before your script) and it's non-sensical. There is no such handler. That should suppress the warning.
Watch out for null bytes. PHP strings can have those in the middle, but Aerospike strings will terminate there, so it'll get truncated at that point. Read the documentation on Handling Unsupported Types, which is why you're provided the \Aerospike\Bytes wrapper class.
Related
The objective is to continually collect data of the current temperature. But a separate process should analyse the output of that data because I have to tweak the algorithm a lot but I want to avoid downtime so stopping the process is a no-go.
The problem is when I separate these processes, that process 2 would either continually have to make calls to the database or read from a local file to do something with the output generated by 1 process but I want to act upon it immediately and that is expensive in terms of resources.
Would it be possible to reload the class into memory somehow when the file changes by for example writing a function that keeps calculating the MD5 of the file, and if it changes than reload the class somehow? So this separate class should act as a plugin. Is there any way to make that work?
Here is a possible solution. Use Beanstalk (https://github.com/kr/beanstalkd).
PHP class to talk to Beanstalk (https://github.com/pda/pheanstalk)
Run beanstalk.
Create a process that goes into an infinite loop that reads from a Beanstalk queue. (Beanstalk queues are called "Tubes"). PHP processes are not meant to be run for a very long time. The main reason for this is memory. The easiest way to do handle this is to restart the process every once in a while or if memory gets to a certain threshold.
NOTE: What I do is to have the process exit after some fixed time or if it uses a certain amount of memory. Then, I use Supervisor to restart it.
You can put data into Beanstalk as JSON and decode it on the receiving end. The sending and receiving processes need to agree on that format. You could store your work payload in a database and just send the primary key in the queue.
Here is some code you can use:
class BeanstalkClient extends AbstractBaseQueue{
public $queue;
public $host;
public $port;
public $timeout;
function __construct($timeout=null) {
$this->loadClasses();
$this->host = '127.0.0.1';
$this->port = BEANSTALK_PORT;
$this->timeout = 30;
$this->connect();
}
public function connect(){
$this->queue = new \Pheanstalk\Pheanstalk($this->host, $this->port);
}
public function publish($tube, $data, $delay){
$payload = $this->encodeData($data);
$this->queue->useTube($tube)->put($payload,
\Pheanstalk\PheanstalkInterface::DEFAULT_PRIORITY, $delay);
}
public function waitForMessages($tube, $callback=null){
if ( $this->timeout ) {
return $this->queue->watchOnly($tube)->reserve($this->timeout);
}
return $this->queue->watchOnly($tube)->reserve();
}
public function delete($message){
$this->queue->delete($message);
}
public function encodeData($data){
$payload = json_encode($data);
return $payload;
}
public function decodeData($encodedData) {
return json_decode($encodedData, true);
}
public function getData($message){
if ( is_string($message) ) {
throw new Exception('message is a string');
}
return json_decode($message->getData(), true);
}
}
abstract class BaseQueueProcess {
protected $channelName = ''; // child class should set this
// The queue object
public $queue = null;
public $processId = null; // this is the system process id
public $name = null;
public $status = null;
public function initialize() {
$this->processId = getmypid();
$this->name = get_called_class();
$this->endTime = time() + (2 * 60 * 60); // restart every hour
// seconds to timeout when waiting for a message
// if the process isn't doing anything, timeout so they have a chance to do housekeeping.
$queueTimeout = 900;
if ( empty($this->queue) ) {
$this->queue = new BeanstalkClient($queueTimeout);
}
}
public function receiveMessage($queueMessage) {
$taskData = $this->queue->getData($queueMessage);
// debuglog(' Task Data = ' . print_r($taskData, true));
if ( $this->validateTaskData($taskData) ) {
// process the message
$good = $this->didReceiveMessage($taskData);
if ( $good !== false ) {
// debuglog("Completing task {$this->taskId}");
$this->completeTask($queueMessage);
}
else {
$this->failTask($queueMessage);
}
}
else {
// Handle bad message
$this->queue->delete($queueMessage);
}
}
public function run() {
$this->processName = $this->channelName;
// debuglog('Start ' . $this->processName);
// debuglog(print_r($this->params, true));
while(1) {
$queueMessage = $this->queue->waitForMessages($this->channelName);
if ( ! empty($queueMessage) ) {
$this->receiveMessage($queueMessage);
}
else {
// empty message
// a timeout
// // debuglog("empty message " . get_called_class());
}
$memory = memory_get_usage();
if( $memory > 20000000 ) {
// debuglog('Exit '.get_called_class().' due to memory. Memory:'. ($memory/1024/1024).' MB');
// Supervisor will restart process.
exit;
}
elseif ( time() > $this->endTime ) {
// debuglog('Exit '.get_called_class().' due to time.');
// Supervisor will restart process.
exit;
}
usleep(10);
}
}
public function completeTask($queueMessage) {
//
$this->queue->delete($queueMessage);
}
public function failTask($queueMessage) {
//
$this->queue->delete($queueMessage);
}
}
class MyProcess extends BaseQueueProcess {
public function initialize() {
$this->channelName = 'Temperature';
parent::initialize();
}
public function didReceiveMessage($taskData) {
// debuglog(print_r($taskData, true));
// process data here
// return false if something went wrong
return true;
}
}
//Sender
class WorkSender {
const TubeName = 'Temperature';
const TubeDelay = 0; // Set delay to 0, i.e. don't use a delay.
function send($data) {
$c = BeanstalkClient();
$c->publish(self::TubeName, $data, self::TubeDelay);
}
}
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'm creating my own custom SessionHandler to store my session information in a postgresql 9.3 database and I'm having a problem where the session data passed to the write() method isn't being written to the database, but the session name is??
Things that I know for a fact
My custom class is handling the sessions when session_start() is called - as tested with echoing output from the various methods and no session files are being created in /tmp
The $session_data arg in write() contains the proper serialized string as shown by echoing the contents in the write() method.
$session_name is being written to the database just fine and so is a BLANK serialized string a:0:{}.
Things I'm confused about:
Echoing the contents of $_SESSION['test_var1'] shows the correct value stored, even if read() is empty or returning no value??
If the session name is saved in the DB just fine, why isn't the session data?
Server Configuration
OS: Gentoo
Database: Postgresql 9.3
Web Server: NGINX 1.7.6
PHP 5.5.18 connected to NGINX via FPM
PHP ini session settings
session.save_handler = user
session.use_strict_mode = 1
session.use_cookies = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.name = _g
session.auto_start = 0
session.serialize_handler = php_serialize
class SessionManagement implements SessionHandlerInterface {
private $_id = '';
private $_link = null;
public function __construct() {
session_set_save_handler(
[$this, 'open'],
[$this, 'close'],
[$this, 'read'],
[$this, 'write'],
[$this, 'destroy'],
[$this, 'gc']
);
}
public function open($save_path, $session_id) {
echo 'open called<br/>';
$this->_id = $session_id;
$this->_link = new PDO('pgsql:host=' . $_SERVER['DB_HOST'] . ';dbname=' . $_SERVER['DB_DB'],
$_SERVER['DB_USER'],
$_SERVER['DB_PASS']);
}
public function close() {
echo 'close called<br/>';
}
public function destroy($session_id) {
echo 'destroying '.$session_id, '<br/>';
}
public function gc($maxlifetime) {
echo 'GC called<br/>';
}
public function read($session_name) {
$name = $this->_id.'_'.$session_name;
$sql = 'SELECT session_data FROM sessions WHERE session_name = :name';
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name])) {
return $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
} else {
return false;
}
} else {
return false;
}
return '';
}
public function write($session_name, $session_data) {
echo 'Session data: '.$session_data.'<br/>';
$name = $this->_id . '_' . $session_name;
$data = $session_data;
$sql = "SELECT 1 FROM sessions WHERE session_name = :name";
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name])) {
if ($rel->rowCount()) {
echo 'Updating...<br/>';
$sql = 'UPDATE sessions SET session_data = :data WHERE session_name = :name';
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name, ':data' => $data])) {
echo 'Update success...<br/>';
} else {
echo 'Update failed...<br/>';
var_dump($rel->errorInfo());
}
}
} else {
echo 'Inserting...<br/>';
$sql = 'INSERT INTO sessions (session_name, session_data) ';
$sql .= 'VALUES(:name, :data)';
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name, ':data' => $data])) {
echo 'Insert success...<br/>';
} else {
echo 'Insert failed...<br/>';
var_dump($rel->errorInfo());
}
}
}
}
}
}
}
Test code:
new SessionManagement();
session_start();
$_SESSION['test_var1'] = 'some test data';
session_write_close(); // Making sure writing is finished
echo $_SESSION['test_var1'];
Output via test page
open called
Session data: a:1:{s:9:"test_var1";s:14:"some test data";}
Inserting...
Insert success...
close called
some test data
Relevant database fields
session_name: _g_h8m64bsb7a72dpj56vgojn6f4k3ncdf97leihcqfupg2qtvpbo20
session_data: a:0:{}
I'm not sure if this is a database issue or a PHP issue. I've been messing with this for a few days now and decided it was time to ask the community. Hopefully someone has some insight as to what the problem is.
I think you must initialize PDO object outside of the Open function handler and the class itself
try to access to your PDO Object with a Global value or through a static variable.
This is my implementation with MYSQL for my project :
class Core_DB_SessionHandler implements SessionHandlerInterface
{
protected $options = array(); // Options de la session
protected static $db = NULL; // Acceder a la BDD
public function __construct($options, $pdo) {
$this->options = $options;
self::$db = $pdo;
}
public function open($savePath, $sessionName) {
$now = time();
$req = self::$db->prepare("DELETE FROM tbl_sessions WHERE expire < '{1}' ");
$req->execute(array($now));
return TRUE;
}
public function close() {
$this->gc(ini_get('session.gc_maxlifetime'));
}
public function read($id) {
$now = time();
$stmt = self::$db->query("SELECT data FROM tbl_sessions WHERE sid = '$id AND expire < '$now'");
$result = $stmt->fetchColumn();
return $result;
}
public function write($id, $data) {
if (array_key_exists('TIMEOUT', $_SESSION)) {
$newExp = $_SESSION['TIMEOUT'];
}
else {
$newExp = time() + $this->options['time_limiter'];
}
try {
$req = self::$db->prepare('INSERT INTO tbl_sessions (sid, data, expire) VALUES (:sid, :data, :expire)
ON DUPLICATE KEY UPDATE data = :data, expire = :expire');
$vals = array('sid' => $id, 'data' => $data, 'expire' => $newExp);
$req->execute($vals);
return TRUE;
}
catch (PDOException $e) {
throw new Core_Exception(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
}
}
public function destroy($id) {
$stmt = self::$db->prepare("DELETE FROM tbl_sessions WHERE sid = '{1}'");
$stmt->execute(array($id));
//return ($stmt->rowCount() === 1) ? true : false;
return TRUE;
}
public function gc($maxlifetime) {
$now = time();
$req = self::$db->prepare("DELETE FROM tbl_sessions WHERE expire < '{1}' ");
$req->execute(array($now));
return TRUE;
}
}
and i initialize handler like this :
$handler = new Core_DB_SessionHandler($MyOptions, $MyPDO);
if (PHP5) {
if (!session_set_save_handler($handler, TRUE)) {
throw new Core_Exception('Erreur lors de l\'init des sessions !');
}
}
nb : In your Table structure don't use autoincrement for ID
Well, I've solved my problem, but it's hard to say if this fix was actually the problem in the first place.
In the read method, I changed the follow:
return $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
to
$data $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
return $data;
After this the session was writing $session_data to the database without any problem. That's all well and dandy, but it doesn't explain why it didn't work in the first place. I mainly say this because upon switching the statement back everything continued to work. As in, I can't reproduce the issue now. So it's hard for me to even say that this was the problem in the first place.
Hopefully this helps someone. I've been unable to find more information about it, but it something does show up I'll be sure to add it here.
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.
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.