Related
It's my first time to use Laravel and Redis. I understand how to get, set, etc of Redis on Terminal. But no idea how to apply Redis on Laravel application.
I have application that saves participant's information in DB with MVC pattern. and I'd like to change it to use Redis cache to make it faster(and for practice). What do I have to do? Could you explain it with code?
This is ParticipantController. 'edit' function send user to edit page, and user edit the information and push 'save', it activate 'update' function. store/updateUserInput functions are just saving data to DB nothing else.
/**
* Show the form for editing the specified participant.
*
* #param int $id
* #return View
*/
public function edit(int $id): View
{
$participant = Participant::find($id);
if(empty($participant)){
return view('errors.404');
}
return view('participants.edit', ['participant'=>$participant]);
}
/**
* Update the specified participant in storage.
*
* #param ParticipantValidation $request
* #return RedirectResponse
*/
public function update(ParticipantValidation $request): RedirectResponse
{
$participant = Participant::find($request->id);
if(empty($participant)){
return view('errors.404');
}
$detail = $request->all();
Participant::updateUserInput($detail);
return redirect()->route('participants.create', $detail['event_id'])->with('success', 'Updated!');
}
+plus I tried this code on top of 'Controller' to use sth like $redis->set('message', 'Hello world'); but there's error that they cannot find 'Predis/Autoload.php'
require 'Predis/Autoload.php';
PredisAutoloader::register();
try {
$redis = new PredisClient();
}
catch (Exception $e) {
die($e->getMessage());
}
You can use the Cache facade
In your .env file, you must add CACHE_DRIVER=redis
Then whenever you want to get an instance of Participant:
$participant = null;
$key ="Participant".$id;
if(Cache::has($key)//get participant from cache
$participant = Cache::get($key);
else{//get participant and cache for 3 minutes
$participant = Participant::find($id);
$seconds = 180;
Cache::set($key, $participant, $seconds);
}
Trying to understand how to share my session data between my App and my chat server (Ratchet). I thought using Symfony & Memcache would be easy enough but I just can't seem to get it working.
I am trying to get the user_id out of the session for when somebody sends a message to the chat it will insert the user_id into the database (Chat->onMessage).
Can somebody point me in the right direction?
Flow:
config.php is included on every page
When user logs into the website it executes the $login->processLogin() method
I start my chat server via command line (php server.php)
config.php
<?php
use MyApp\Login;
use MyApp\Session as MySession;
# Define backslash or forward slash for *NIX and IIS systems.
define('DS', DIRECTORY_SEPARATOR);
# Attempt to determine the full-server path to the 'root' folder in order to reduce the possibility of path problems.
define('BASE_PATH', realpath(dirname(__FILE__)).DS);
# Define the complete path to the root of the domain we are at (ie. /home/user/domain.com) (does't end in a slash)
define('ROOT_PATH', $_SERVER['DOCUMENT_ROOT']);
# Define where cookies may be active. ('/' means the entire domain)
define('COOKIE_PATH', '/');
# Name sessions. (needs to be alphanumeric with no periods[.]- can't be solely digits; must contain at least one letter)
define('SESSIONS_NAME', 'SiteUser');
# Get the Session Class.
require_once BASE_PATH.'modules'.DS.'Session'.DS.'Session.php';
# Check if there is a session id set the the $sesh_id variable.
$sesh_id=((isset($sesh_id)) ? $sesh_id : NULL);
# Create a new session object, thus starting a new session.
$mysession=MySession::getInstance(NULL, NULL, NULL, $sesh_id);
Login
<?php
namespace MyApp;
use Exception;
# Make sure the script is not accessed directly.
if(!defined('BASE_PATH'))
{
exit('No direct script access allowed');
}
# Get the User Class
require_once BASE_PATH.'modules'.DS.'Login'.DS.'User.php';
/**
* Class Login
*
* The Login Class is used to login in and out users as well as checking various login privileges.
*/
class Login extends User
{
/**
* processLogin
*
* Checks if the Login has been submitted and processes it.
*
* #access public
*/
public function processLogin()
{
if($this->isLoggedIn()===TRUE)
{
header("location: main.php");
die;
}
# Check if the form has been submitted.
if($_SERVER['REQUEST_METHOD']=='POST')
{
try
{
try
{
$this->setLoginSessions($this->getID(), TRUE);
header("location: main.php");
}
catch(Exception $e)
{
throw $e;
}
}
catch(Exception $e)
{
throw $e;
}
}
}
/**
* Checks if user is logged in or not. Returns TRUE if logged in, FALSE if not.
*
* #return bool
*/
public function isLoggedIn()
{
global $mysession;
$symfony_session=$mysession->symfony_session;
//if(!isset($_SESSION['user_logged_in']))
if(!$symfony_session->has('user_id'))
{
# Check if we have a cookie
if(isset($_COOKIE['cookie_id']))
{
try
{
$this->setID($_COOKIE['cookie_id']);
}
catch(Exception $e)
{
unset($_COOKIE['user_ip']);
unset($_COOKIE['athenticate']);
unset($_COOKIE['cookie_id']);
return FALSE;
}
}
else
{
return FALSE;
}
}
//elseif($_SESSION['user_logged_in']===TRUE)
if($symfony_session->get('user_logged_in')===TRUE)
{
return TRUE;
}
return FALSE;
}
/**
* Sets the login sessions.
*
* #param null $user_id
* #param null $logged_in
* #param bool $secure
* #throws Exception
*/
public function setLoginSessions($user_id=NULL, $logged_in=NULL, $secure=FALSE)
{
global $mysession;
$symfony_session=$mysession->symfony_session;
# Check if the user is logged in.
if($this->isLoggedIn()===TRUE)
{
if($user_id===NULL)
{
try
{
# Get the User's data.
$this->findUserData();
$user_id=$this->getID();
$logged_in=TRUE;
}
catch(Exception $e)
{
throw $e;
}
}
}
$symfony_session->set('user_id', $user_id);
$symfony_session->set('user_logged_in', $logged_in);
/*
# Set the User's login sessions.
$_SESSION['user_id']=$user_id;
$_SESSION['user_logged_in']=$logged_in;
*/
}
}
Session
<?php
namespace MyApp;
use Memcache;
use Symfony\Component\HttpFoundation\Session\Session as SymfonySession;
use Symfony\Component\HttpFoundation\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
/**
* Class Session
*
* The Session class is used to access and manipulate Sessions and data stored in them.
*/
class Session
{
private static $session;
private $message=FALSE;
private $sessname=FALSE;
public $symfony_session;
/**
* Session constructor.
*
* Safely calls session_start().
* Also enables sessions to span sub domains. It names the session (which is necessary for
* session_set_cookie_params() to work). If calling this class before setting.php, $sessname (the session name) AND
* $cookiepath (the path for cookies) MUST be defined.
*
* #param null $sessname
* #param null $cookiepath
* #param bool $secure
* #param null $sesh_id
*/
public function __construct($sessname=NULL, $cookiepath=NULL, $secure=FALSE, $sesh_id=NULL)
{
require_once BASE_PATH.'vendor'.DS.'autoload.php';
$memcache=new Memcache;
$memcache->connect(DOMAIN_NAME, 11211);
$storage=new NativeSessionStorage(array(), new Handler\MemcacheSessionHandler($memcache));
$symfony_session=new SymfonySession($storage);
# Check if a session ID was passed.
if($sesh_id!==NULL)
{
//session_id($sesh_id);
$symfony_session->setId($sesh_id);
}
# Is a session already started?
//if(!isset($_SESSION['s_set']))
if(!$symfony_session->has('s_set'))
{
# If we haven't been given a session name, we will give it one.
if(empty($cookiepath))
{
# Set the default cookie path be the root of the site.
$cookiepath=DS;
# Check if the cookie path was defined in settings.php.
if(defined('COOKIE_PATH'))
{
# Check if the defined path is blank.
if(COOKIE_PATH!='')
{
# If the cookie path has been defined in settings.php, we'll use that path.
$cookiepath=COOKIE_PATH;
}
}
}
//session_set_cookie_params($life, $cookiepath, '.'.DOMAIN_NAME, $secure);
/*
* Read the current save path for the session files and append our own directory to this path.
* Note: In order to make that platform independent, we need to check for the file-seperator first.
* Now we check if the directory already has been created, if not, create one.
* Then we set the new path for the session files.
*/
# Get the session save path.
$save_path=session_save_path();
# Find out if our custom_session folder exists. If not, let's make it.
if(!is_dir(BASE_PATH.'../custom_sessions'.DS.'.'))
{
mkdir(BASE_PATH.'../custom_sessions', 0755);
}
# Is our custom_sessions folder the session save path? If not, let's make it so.
if($save_path!==BASE_PATH.'../custom_sessions')
{
//session_save_path(BASE_PATH.'../custom_sessions');
# How do I set the save path in Symfony?
}
# If we haven't been given a session name, we will give it one.
if(empty($sessname))
{
# Set the default session name.
$sessname='PHPSESSID';
# Check if the session name was defined in settings.php.
if(defined('SESSIONS_NAME'))
{
# Check if the defined name is blank.
if(SESSIONS_NAME!='')
{
# If the session name has been defined in settings.php, we'll give the session that name.
$sessname=SESSIONS_NAME;
}
}
}
$storage->setOptions(array(
'cookie_domain'=>'.'.DOMAIN_NAME,
'cookie_lifetime'=>0,
'cookie_path'=>$cookiepath,
'cookie_secure'=>$secure,
'name'=>$sessname
));
//$this->setSessname($sessname);
# Name the session.
//session_name($this->getSessname());
# Session must be started before anything.
//session_start();
//$session->setName($this->getSessname());
$symfony_session->start();
# Set the s_set session so we can tell if session_start has been called already.
//$_SESSION['s_set']=1;
$symfony_session->set('s_set', 1);
}
$this->symfony_session=$symfony_session;
print_r($symfony_session);exit;
}
/**
* getSessname
*
* Returns the data member $sessname.
*
* #access public
*/
public function getSessname()
{
return $this->sessname;
}
/**
* Sets the data member $sessname. If an empty value is passed, the data member will
* be set with FALSE. Returns the set data member value.
*
* #param $sessname
* #return bool
*/
public function setSessname($sessname)
{
# Clean it up...
$sessname=trim($sessname);
# Check if the passed value is now empty.
if(empty($sessname))
{
# Explicitly set the data member to false.
$sessname=FALSE;
}
# Set the data member.
$this->sessname=$sessname;
# Return the data member after it has gone through the get method.
return $this->getSessname();
}
/**
* Gets the singleton instance of this class.
*
* #param null $sessname
* #param null $cookiepath
* #param bool $secure
* #param null $sesh_id
* #return Session
*/
public static function getInstance($sessname=NULL, $cookiepath=NULL, $secure=FALSE, $sesh_id=NULL)
{
if(!self::$session)
{
self::$session=new Session($sessname, $cookiepath, $secure, $sesh_id);
}
return self::$session;
}
}
server.php
<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use Ratchet\Session\SessionProvider;
use Symfony\Component\HttpFoundation\Session\Storage\Handler;
use MyApp\Chat;
$port="8080";
# Change to this directory.
chdir(dirname(__FILE__));
# Need this for the database insert.
if(!defined('DOMAIN_NAME'))
{
define('DOMAIN_NAME', 'example.dev');
}
require_once '../../includes/lonconfig.php';
require_once '../../vendor/autoload.php';
$memcache=new Memcache;
$memcache->connect(DOMAIN_NAME, 11211);
$session=new SessionProvider(
new Chat,
new Handler\MemcacheSessionHandler($memcache)
);
$server=IoServer::factory(
new HttpServer(
new WsServer($session)
),
$port,
DOMAIN_NAME
);
$server->run();
Chat
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface
{
protected $clients=array();
public function onOpen(ConnectionInterface $conn)
{
# get the cookies
$cookies=(string)$conn->WebSocket->request->getHeader('Cookie');
# Returns only PHPSESSID (not SiteUser).
//var_dump($cookies);exit;
$this->clients[$conn->resourceId]=$conn;
echo "New connection! ({$conn->resourceId})\n";
}
/**
* #param ConnectionInterface $conn
* #param string $data
*/
public function onMessage(ConnectionInterface $conn, $data)
{
$database=$this->dbh;
$data=json_decode($data, TRUE);
if(isset($data['data']) && count($data['data'])!=0)
{
require_once BASE_PATH.'vendor'.DS.'autoload.php';
$memcache=new Memcache;
$memcache->connect(DOMAIN_NAME, 11211);
$storage=new NativeSessionStorage(array(), new Handler\MemcacheSessionHandler($memcache));
$session=new SymfonySession($storage);
$type=$data['type'];
$user_id=$conn->Session->get('user_id');
$return=NULL;
if($type=="send" && isset($data['data']['type']) && $user_name!=-1)
{
$msg=htmlspecialchars($data['data']['msg']);
$date=new DateTime;
$date->setTimezone(new DateTimeZone(TIMEZONE));
if($data['data']['type']=='text')
{
echo 'test 4';exit;
$database->query('SELECT `id`, `user_id`, `msg`, `type` FROM `chat` ORDER BY `id` DESC LIMIT 1', array());
$lastMsg=$database->statement->fetch(PDO::FETCH_OBJ);
if($lastMsg->user_id==$user_id && (strlen($lastMsg->msg)<=100 || strlen($lastMsg->msg)+strlen($msg)<=100))
{
# Append message.
$msg=$lastMsg->msg."<br/>".$msg;
$database->query('UPDATE `chat` SET `msg`=:msg, `posted`=NOW() WHERE `id`=:lastmsg', array(
':msg'=>$msg,
':lastmsg'=>$lastMsg->id
));
$return=array(
"id"=>$lastMsg->id,
"name"=>$user_name['staffname'],
"type"=>"text",
"msg"=>$msg,
"posted"=>$date->format("Y-m-d H:i:sP"),
"append"=>TRUE
);
}
else
{
$database->query('INSERT INTO `chat` (`user_id`, `msg`, `type`, `posted`) VALUES (?, ?, "text", NOW())', array(
$user_id,
$msg
));
# Get last insert ID.
$get_chat_id=$database->lastInsertId();
$return=array(
"id"=>$get_chat_id,
"name"=>$user_name['staffname'],
"type"=>"text",
"msg"=>$msg,
"posted"=>$date->format("Y-m-d H:i:sP")
);
}
}
foreach($this->clients as $client)
{
$this->send($client, "single", $return);
}
}
elseif($type=="fetch")
{
# Fetch previous messages.
$this->fetchMessages($conn, $data['data']['id']);
}
}
}
}
You shouldn't actually share that data over all parts of your application.
If you open the tutorial of Ratchet, you will find a part about pushServers.
This allows you to push to a certain channel or $user_id(in your case)
This makes you able to not save or transfer the data in the two parts and will facilitate you to have a streamlined workflow.
I personally use the pushServer in multiple channels, they are split up into:
All users online (sendBroadcast)
All users in a group (sendGroup)
All users following a tag (sendTag)
To other users(sendToUser)
I hope this already gives you an idea on how to solve your problem, otherwise feel free to ask.
I use Redis server for sharing session between Php and Node js. For Node js client use "connect-redis" and for php client use redis-session-php and Predis. I took most of code from here gist upgraded version on stack (from correct answer).
app.js
var express = require('express'),
app = express(),
cookieParser = require('cookie-parser'),
session = require('express-session'),
RedisStore = require('connect-redis')(session);
app.use(express.static(__dirname + '/public'));
app.use(function(req, res, next) {
if (~req.url.indexOf('favicon'))
return res.send(404);
next();
});
app.use(cookieParser());
app.use(session({
store: new RedisStore({
// this is the default prefix used by redis-session-php
prefix: 'session:php:'
}),
// use the default PHP session cookie name
name: 'PHPSESSID',
secret: 'node.js rules',
resave: false,
saveUninitialized: false
}));
app.use(function(req, res, next) {
req.session.nodejs = 'Hello from node.js!';
res.send('<pre>' + JSON.stringify(req.session, null, ' ') + '</pre>');
});
app.listen(8080);
app.php
<?php
// this must match the express-session `secret` in your Express app
define('EXPRESS_SECRET', 'node.js rules');
// ==== BEGIN express-session COMPATIBILITY ====
// this id mutator function helps ensure we look up
// the session using the right id
define('REDIS_SESSION_ID_MUTATOR', 'express_mutator');
function express_mutator($id) {
if (substr($id, 0, 2) === "s:")
$id = substr($id, 2);
$dot_pos = strpos($id, ".");
if ($dot_pos !== false) {
$hmac_in = substr($id, $dot_pos + 1);
$id = substr($id, 0, $dot_pos);
}
return $id;
}
// check for existing express-session cookie ...
$sess_name = session_name();
if (isset($_COOKIE[$sess_name])) {
// here we have to manipulate the cookie data in order for
// the lookup in redis to work correctly
// since express-session forces signed cookies now, we have
// to deal with that here ...
if (substr($_COOKIE[$sess_name], 0, 2) === "s:")
$_COOKIE[$sess_name] = substr($_COOKIE[$sess_name], 2);
$dot_pos = strpos($_COOKIE[$sess_name], ".");
if ($dot_pos !== false) {
$hmac_in = substr($_COOKIE[$sess_name], $dot_pos + 1);
$_COOKIE[$sess_name] = substr($_COOKIE[$sess_name], 0, $dot_pos);
// https://github.com/tj/node-cookie-signature/blob/0aa4ec2fffa29753efe7661ef9fe7f8e5f0f4843/index.js#L20-L23
$hmac_calc = str_replace("=", "", base64_encode(hash_hmac('sha256', $_COOKIE[$sess_name], EXPRESS_SECRET, true)));
if ($hmac_calc !== $hmac_in) {
// the cookie data has been tampered with, you can decide
// how you want to handle this. for this example we will
// just ignore the cookie and generate a new session ...
unset($_COOKIE[$sess_name]);
}
}
} else {
// let PHP generate us a new id
session_regenerate_id();
$sess_id = session_id();
$hmac = str_replace("=", "", base64_encode(hash_hmac('sha256', $sess_id, EXPRESS_SECRET, true)));
// format it according to the express-session signed cookie format
session_id("s:$sess_id.$hmac");
}
// ==== END express-session COMPATIBILITY ====
require('redis-session-php/redis-session.php');
RedisSession::start();
$_SESSION["php"] = "Hello from PHP";
if (!isset($_SESSION["cookie"]))
$_SESSION["cookie"] = array();
echo "<pre>";
echo json_encode($_COOKIE, JSON_PRETTY_PRINT);
echo json_encode($_SESSION, JSON_PRETTY_PRINT);
echo "</pre>";
?>
Problem is that: When first execute "php file" then execute "node js server page" - "node js server page" have not seen session creation from "php file". When vice versa (first execute "node js server page" then execute "php file") session variables have seen in both page
result app.php
[]{
"php": "Hello from PHP",
"cookie": []
}
result node js page (http://127.0.0.1:8080)
{
"cookie": {
"originalMaxAge": null,
"expires": null,
"httpOnly": true,
"path": "/"
},
"nodejs": "Hello from node.js!"
}
You are probably facing a cross domain issue. If you are running PHP and Node in a different address or port than PHP (and probably you are), HTML won't share Cookies between requests that go to the other domain, it will keep separated copies to each domain.
If you are using subdomains (for example, your PHP in a URL like app1.mydomain.com and your NodeJS running in app2.mydomain.com), you can share your cookies configuring them to be set/read using the main domain cookie path ( mydomain.com ).
There is good information about this topic over here:
Using Express and Node, how to maintain a Session across subdomains/hostheaders.
Let me us if you need more information or if your problem is not exactly that one.
i solve this problem from this article: PHP and Node.JS session share using Redis
app.js
var app = require("http").createServer(handler),
fs = require("fs"),
redis = require("redis"),
co = require("./cookie.js");
app.listen(443);
//On client incomming, we send back index.html
function handler(req, res) {
//Using php session to retrieve important data from user
var cookieManager = new co.cookie(req.headers.cookie);
//Note : to specify host and port : new redis.createClient(HOST, PORT, options)
//For default version, you don't need to specify host and port, it will use default one
var clientSession = new redis.createClient();
console.log('cookieManager.get("PHPSESSID") = ' + cookieManager.get("PHPSESSID"));
clientSession.get("sessions/" + cookieManager.get("PHPSESSID"), function(error, result) {
console.log("error : " + result);
if(error) {
console.log("error : " + error);
}
if(result != null) {
console.log("result exist");
console.log(result.toString());
} else {
console.log("session does not exist");
}
});
//clientSession.set("sessions/" + cookieManager.get("PHPSESSID"), '{"selfId":"salamlar22", "nodejs":"salamlar33"}');
}
cookie.js
//Directly send cookie to system, if it's node.js handler, send :
//request.headers.cookie
//If it's socket.io cookie, send :
//client.request.headers.cookie
module.exports.cookie = function(co){
this.cookies = {};
co && co.split(';').forEach(function(cookie){
var parts = cookie.split('=');
this.cookies[parts[0].trim()] = (parts[1] || '').trim();
}.bind(this));
//Retrieve all cookies available
this.list = function(){
return this.cookies;
};
//Retrieve a key/value pair
this.get = function(key){
if(this.cookies[key]){
return this.cookies[key];
}else{
return {};
}
};
//Retrieve a list of key/value pair
this.getList = function(map){
var cookieRet = {};
for(var i=0; i<map.length; i++){
if(this.cookies[map[i]]){
cookieRet[map[i]] = this.cookies[map[i]];
}
}
return cookieRet;
};
};
app.php
<?php
include 'redis.php';
session_start();
echo '<pre>';
var_dump($_COOKIE);
echo '</pre>';
echo '$_SESSION["nodejs"] = '.$_SESSION[selfId].'<br>';
$_SESSION[selfId] = 2;
echo '$_SESSION["nodejs"] = '.$_SESSION[selfId].'<br>';
?>
redis.php
<?php
//First we load the Predis autoloader
//echo dirname(__FILE__)."/predis-1.0/src/Autoloader.php";
require(dirname(__FILE__)."/redis-session-php/modules/predis/src/Autoloader.php");
//Registering Predis system
Predis\Autoloader::register();
/**
* redisSessionHandler class
* #class redisSessionHandler
* #file redisSessionHandler.class.php
* #brief This class is used to store session data with redis, it store in json the session to be used more easily in Node.JS
* #version 0.1
* #date 2012-04-11
* #author deisss
* #licence LGPLv3
*
* This class is used to store session data with redis, it store in json the session to be used more easily in Node.JS
*/
class redisSessionHandler{
private $host = "127.0.0.1";
private $port = 6379;
private $lifetime = 0;
private $redis = null;
/**
* Constructor
*/
public function __construct(){
$this->redis = new Predis\Client(array(
"scheme" => "tcp",
"host" => $this->host,
"port" => $this->port
));
session_set_save_handler(
array(&$this, "open"),
array(&$this, "close"),
array(&$this, "read"),
array(&$this, "write"),
array(&$this, "destroy"),
array(&$this, "gc")
);
}
/**
* Destructor
*/
public function __destruct(){
session_write_close();
$this->redis->disconnect();
}
/**
* Open the session handler, set the lifetime ot session.gc_maxlifetime
* #return boolean True if everything succeed
*/
public function open(){
$this->lifetime = ini_get('session.gc_maxlifetime');
return true;
}
/**
* Read the id
* #param string $id The SESSID to search for
* #return string The session saved previously
*/
public function read($id){
$tmp = $_SESSION;
$_SESSION = json_decode($this->redis->get("sessions/{$id}"), true);
if(isset($_SESSION) && !empty($_SESSION) && $_SESSION != null){
$new_data = session_encode();
$_SESSION = $tmp;
return $new_data;
}else{
return "";
}
}
/**
* Write the session data, convert to json before storing
* #param string $id The SESSID to save
* #param string $data The data to store, already serialized by PHP
* #return boolean True if redis was able to write the session data
*/
public function write($id, $data){
$tmp = $_SESSION;
session_decode($data);
$new_data = $_SESSION;
$_SESSION = $tmp;
$this->redis->set("sessions/{$id}", json_encode($new_data));
$this->redis->expire("sessions/{$id}", $this->lifetime);
return true;
}
/**
* Delete object in session
* #param string $id The SESSID to delete
* #return boolean True if redis was able delete session data
*/
public function destroy($id){
return $this->redis->delete("sessions/{$id}");
}
/**
* Close gc
* #return boolean Always true
*/
public function gc(){
return true;
}
/**
* Close session
* #return boolean Always true
*/
public function close(){
return true;
}
}
new redisSessionHandler();
?>
As I've finally found a binary of memcache for PHP 5.4.4 on Windows, I'm speeding up the application I'm currently developing.
I've succeeded setting memcache as Doctrine ORM Mapping Cache driver, but I need to fix another leakage: Forms built using annotations.
I'm creating forms according to the Annotations section of the docs. Unfortunately, this takes a lot of time, especially when creating multiple forms for a single page.
Is it possible to add caching to this process? I've browsed through the code but it seems like the Zend\Form\Annotation\AnnotationBuilder always creates the form by reflecting the code and parsing the annotations. Thanks in advance.
You might wanna try something like this:
class ZendFormCachedController extends Zend_Controller_Action
{
protected $_formId = 'form';
public function indexAction()
{
$frontend = array(
'lifetime' => 7200,
'automatic_serialization' => true);
$backend = array('cache_dir' => '/tmp/');
$cache = Zend_Cache::factory('Core', 'File', $frontend, $backend);
if ($this->getRequest()->isPost()) {
$form = $this->getForm(new Zend_Form);
} else if (! $form = $cache->load($this->_formId)) {
$form = $this->getForm(new Zend_Form);
$cache->save($form->__toString(), $this->_formId);
}
$this->getHelper('layout')->setLayout('zend-form');
$this->view->form = $form;
}
Found here.
Louis's answer didn't work for me so what I did was simply extend AnnotationBuilder's constructor to take a cache object and then modified getFormSpecification to use that cache to cache the result. My function is below..
Very quick work around...sure it could be improved. In my case, I was limited to some old hardware and this took the load time on a page from 10+ seconds to about 1 second
/**
* Creates and returns a form specification for use with a factory
*
* Parses the object provided, and processes annotations for the class and
* all properties. Information from annotations is then used to create
* specifications for a form, its elements, and its input filter.
*
* MODIFIED: Now uses local cache to store parsed annotations
*
* #param string|object $entity Either an instance or a valid class name for an entity
* #throws Exception\InvalidArgumentException if $entity is not an object or class name
* #return ArrayObject
*/
public function getFormSpecification($entity)
{
if (!is_object($entity)) {
if ((is_string($entity) && (!class_exists($entity))) // non-existent class
|| (!is_string($entity)) // not an object or string
) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects an object or valid class name; received "%s"',
__METHOD__,
var_export($entity, 1)
));
}
}
$formSpec = NULL;
if ($this->cache) {
//generate cache key from entity name
$cacheKey = (is_string($entity) ? $entity : get_class($entity)) . '_form_cache';
//get the cached form annotations, try cache first
$formSpec = $this->cache->getItem($cacheKey);
}
if (empty($formSpec)) {
$this->entity = $entity;
$annotationManager = $this->getAnnotationManager();
$formSpec = new ArrayObject();
$filterSpec = new ArrayObject();
$reflection = new ClassReflection($entity);
$annotations = $reflection->getAnnotations($annotationManager);
if ($annotations instanceof AnnotationCollection) {
$this->configureForm($annotations, $reflection, $formSpec, $filterSpec);
}
foreach ($reflection->getProperties() as $property) {
$annotations = $property->getAnnotations($annotationManager);
if ($annotations instanceof AnnotationCollection) {
$this->configureElement($annotations, $property, $formSpec, $filterSpec);
}
}
if (!isset($formSpec['input_filter'])) {
$formSpec['input_filter'] = $filterSpec;
}
//save annotations to cache
if ($this->cache) {
$this->cache->addItem($cacheKey, $formSpec);
}
}
return $formSpec;
}
I want to store all my sessions in a DB and have read up on this and implemented the following class:
<?php
/**
* This class handles users sessions and stores the session in the DB rather than in a file. This way stops any
* shared host security problems that could potentially happen.
*/
class sessionHandler {
/**
* Initial constructor which takes the database object as a param, to do all the database stuff
* #param object $db The datase object
*/
public function __construct ($db) {
$this->db = $db;
$this->setHandler();
}
function setHandler() {
session_set_save_handler(array(&$this, "open"),
array(&$this, "close"),
array(&$this, "read"),
array(&$this, "write"),
array(&$this, "destroy"),
array(&$this, "clean")
);
}
/**
* Initiate a database object if necessary
*/
function open() {
$this->db->connect();
}
/**
* Write session id and data to the database
* #param string $id The hashed 32 char session id, unique to a user
* #param string $data Serialized session array from the unique session
* #return id The newly inserted ID of the database
*/
function write($id, $data) {
$access = time();
$dateAdded = date("Y-m-d G:i:s");
$this->db->wrapper->where(array("sessionId"=>$id));
$this->db->query($this->db->wrapper->delete(__CLASS__));
//fopen a file and store this in it that way we can debug
$query = $this->db->wrapper->insert(__CLASS__, array("sessionId"=>$id,"dateAdded"=>$dateAdded,"sessionData"=>$data));
$this->db->query($query);
return $this->db->insertId();
}
/**
* Retrieve the session data for a given session id
* #param string $id The hashed 32 char session id, unique to a user
* #return string The session data found for the given session id
*/
function read($id) {
$id = $this->db->wrapper->escape($id);
$row = $this->db->fetch(1, $this->db->wrapper->get_where(__CLASS__,array("sessionId"=>$id)), array(),false);
if ($row) {
return $row['data'];
}
return "";
}
/**
* Delete a session from the database by its unique session id
* #param string $id The hashed 32 char session id, unique to a user
* #return integer The number of deleted rows - should only ever be 1
*/
function destroy($id) {
$id = $this->db->wrapper->escape($id);
$this->db->wrapper->where(array("sessionId"=>$id));
$this->db->query($this->db->wrapper->delete(__CLASS__));
return $this->db->affectedRows();
}
/**
* Garage collector which deletes old records in the database, delete sessions that have expired. This is
* determined by the session.gc_maxlifetime variable in the php.ini
* #param integer $max The maximum number of seconds allowed before a session is to be considered expired
* #return integer The number of deleted rows
*/
function clean($max) {
$old = time() - $max;
$old = $this->db->wrapper->escape($old);
$this->db->wrapper->where(array("access"=>$old), "<");
$this->db->query($this->db->wrapper->delete(__CLASS__));
return $this->db->affectedRows();
}
/**
* Close the database connection once a read / write has been complete
*/
function close() {
$this->db->close();
}
/**
* End the current session and store session data.
*/
public function __destruct(){
session_write_close();
}
}
As you can see in my code, i pass the DB object into the class as a param. My bootstrap file is as follows:
$db = mogDB::init();
$sh = new sessionHandler($db);
session_start();
I have used a few of my own classes here so MogDB:init() basically creates a database connection with the right credentials. The wrapper stuff is basically so i dont have to type out sql query after sql query (me being a bit lazy i guess).
But the problem i get is this in my php error log:
08-Apr-2010 17:40:31] PHP Warning: mysql_insert_id(): 11 is not a valid MySQL-Link resource in /library/mysql.php on line 69
I have debugged this as much as i can and it seems that when it tries to write the session to the DB, it is failing. I have managed to save the query to a file and that imports fine into the database via phpmyadmin, so its not the query thats the problem.
line 69 in mysql.php is as follows:
68. public function insertId() {
69. return mysql_insert_id($this->id);
70. }
Any help would be much appreciated
Thanks
After you include the contents of the file /library/mysql.php around line 69 so we can see what is happening there, I am guessing that a small bit of your code is asking for a database handle back to call mysql_insert_id on, and instead, your db api is giving back the actual insertID already...