I'm using phpredis in my program, store something in the redis server, get them when the same request comes(in the same day), but I always get empty result. Can anyone give me some enlightenment? Here is the code of Cache class I'm using:
<?php
class Cache
{
public static function getInstance()
{
static $instance = null;
null == $instance && $instance = new self();
return $instance;
}
protected function __construct()
{
}
protected function getR()
{
static $r = NULL;
if (NULL == $r) {
$r = new Redis();
try {
$r->pconnect(HOST, PORT, 5);
} catch(Exception $ex) {
//log
try {
$api->connect(HOST, PORT, 5);
} catch (Exception $ex) {
//log
}
}
}
return $r;
}
public function getValue($key)
{
$result = array();
$r = $this->getR();
if(!empty($r)) {
try{
$result = $r->hKeys($key);
$r->setTimeout($keys, 86400);
} catch (Exception $ex){
//log
}
}
return $result; // return true
}
public function setValue($key, $value)
{
$result = false;
$r = $this->getR();
if(!empty($r)) {
try{
$result = $r->hMset($key, $value);
} catch (Exception $ex){
//log
}
}
}
}
?>
EDIT:
I checked the key-values with redis-cli, found something wired: the key-value data was stored in db 5 while I thought it should be in DB 0 by default without select statement, but the program retrieved db 0, of course nothing returned. Now I'm wondering why the data went to DB 5 given that I've not selected DB.
Finally, I've figured out what happend here. Before I stored my key–value pair, there was some code which also had communicated with Redis server and it had explicitly selected the DB 5, and the default DB of my redis connection was affected by last context, so my data was stored in DB 5. By coincidence, when I wanted to retrive my data, the last redis connection used DB 0, of course I got nothing.
Related
I have to subscribe to all the channels of my Redis db and simultaneously read data from another hash in the same db node. The following is the code I have written for this using phpredis:
$notif = new Worker;
try {
// connect redis
$notif->connectRedisServer();
// start the worker processing
$notif->startWorkerProcess();
}
catch(Exception $e) {
exit('Exception: '.$e->getMessage());
}
class Worker {
private $redis;
public $recentHash = 'tracker.data.recent';
public $notifHash = 'tracker.settings.data';
public $serverConnectionDetails = { //Redis server connection details
};
private $redis;
// redis database used for getting recent record of every imei
const RECENTDATA_DB = 1;
public function connectRedisServer() {
if(!empty($this->redis))
return $this->redis; // already connected
$this->redis = new Redis();
if(!$this->redis->pconnect(
$this->serverConnectionDetails['redis']['host'],
$this->serverConnectionDetails['redis']['port']
))
return 'Redis connection failed.';
if(isset($this->serverConnectionDetails['redis']['password'])
&&
!$this->redis->auth($this->serverConnectionDetails['redis']['password'])
)
return 'Redis authentication failed.';
return $this->redis;
}
public function startWorkerProcess() {
$this->redis->select(self::RECENTDATA_DB);
$this->redis->pSubscribe(array('*'), array($this, 'processRedisData'));
}
public function processRedisData($redis, $pattern, $chan, $msg) {
$message = rtrim((string) $msg,",");
$tData = json_decode($message, true);
$tId = (int) $tData['tID'];
echo "Recent Published Data:";
var_dump($tData);
$data = $this->getNotifSettings($tId);
if(!empty($data)) {
echo "Redis Settings: ";
var_dump($trackerSettings);
}
}
public function getNotifSettings($tId) {
$data = $this->redis->hGet($this->notifHash, $tId); //This command doesn't return any data even if it exists for the $tId
if($data === false)
return null;
$data = json_decode($data, true);
return $data; // Always comes up as null
}
}
The problem here is that once I get subscribed to all the channels on db1 in Redis. I don't get any result if I try to run HGET, even though the data for the given key exists in the db. I have put additional comments in the code above to explain where the problem is. Check getNotifSettings() function.
Any help will be appreciated.
I've searched on stackoverflow and other sources but I cant seem to find the issue that is preventing my PHP script from working.
Look at the echo_sql. It produces a healthy update statement which when run updates the database with no problem. Here is a sample:
update waste set waste_name=1 where id =82;
However, when the script is run, it does not apply changes to the database. Here is the script:
if ($_SERVER['REQUEST_METHOD'] == "POST") {
try {
$waste_id = $_POST['waste_id'];
$sql = new db;
$sql->beginTransaction();
$waste_name = $_POST['waste_name'];
$sql->query("update waste set waste_name=:waste_name where id =:waste_id;");
$echo_sql = "update waste set waste_name=$waste_name where id =$waste_id;";
echo $echo_sql;
$sql->bind(':waste_name', $waste_name);
$sql->execute();
$sql->endTransaction();
} catch (Exception $e) {
$sql->rollBack();
echo "Failed: " . $e->getMessage();
}
}
Additional details:
errorCode() = 00000
DB Class:
class db
{
private $stmt;
private $dbc;
public function __construct()
{
$u = "root";
$p = "";
try {
$this->dbc = new PDO('mysql:host=127.0.0.1;dbname=wimsdb', $u, $p);
$this->dbc->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
$e->getMessage();
}
}
public function bind($param, $value, $type = NULL)
{
$this->stmt->bindParam($param, $value, $type);
}
public function beginTransaction()
{
return $this->dbc->beginTransaction();
}
public function rollBack()
{
return $this->dbc->rollBack();
}
public function endTransaction()
{
return $this->dbc->commit();
}
public function cancelTransaction()
{
return $this->dbc->rollBack();
}
public function execute()
{
try {
return $this->stmt->execute();
} catch (PDOException $e) {
return $e->errorInfo;
}
}
public function errorCode()
{
return $this->stmt->errorCode();
}
public function query($query)
{
$this->stmt = $this->dbc->prepare($query);
}
}
Please offer your suggestions on how this could be resolved.
You need to bind the :waste_id too:
$waste_id = $_POST['waste_id'];
$sql = new db;
$sql->beginTransaction();
$waste_name = $_POST['waste_name'];
$sql->query("update waste set waste_name=:waste_name where id =:waste_id;");
$sql->bind(':waste_name', $waste_name);
$sql->bind(':waste_id', $waste_id);
Any time you have an issue like this your error checking should return a meaningful message letting you know where the error is and likely what the error is. You should be able to check your error logs for details and/or output them to your screen during testing.
Add waste_id. To avoid missing parameters, I like putting the parameteers into the execute method. The bind method could be defined anywhere in the code so I had to look through your code and make sure waste_id binding wasn't defined somewhere else. When it's in the execute method, you can quickly see all parameters being defined there...it's also a tad more concise...but both have their uses.
if ($_SERVER['REQUEST_METHOD'] == "POST") {
try {
$waste_id = $_POST['waste_id'];
$sql = new db;
$sql->beginTransaction();
$waste_name = $_POST['waste_name'];
$sql->query("update waste set waste_name=:waste_name where id =:waste_id;");
$echo_sql = "update waste set waste_name=$waste_name where id =$waste_id;";
echo $echo_sql;
//just because I like this syntax for being concise and clear :)
$sql->execute(array(
'waste_id' => $waste_id,
'waste_name' => $waste_name
));
$sql->endTransaction();
} catch (Exception $e) {
$sql->rollBack();
echo "Failed: " . $e->getMessage();
}
I have two PHP scripts that I included below. Both of them attempt to do the same thing, but one works and one does not. I'm looking for someone to explain what PHP is doing under the covers. I'm new to PHP and I suspect that my Java experience is poisoning my thought process when I work in PHP.
What I'm attempting to do is functionally very simple -- Insert a question into a mySQL database table, retrieve the primary key of the inserted row, and then insert five answers into another table with a foreign key relationship to the question.
My original logic looked like this:
ManageQuestions.php:
<?php
session_start();
include('query.php');
echo "begin <br>";
if (isset($_POST['submit'])) {
echo "manageQuestion <br>";
$query = new Query;
$query->createTransaction();
$query->executeCreateUpdateDelete("INSERT INTO question (question) VALUES ('".$_POST['question']."'); ");
$question_pid = $query->getLastInsertedId();
$query->commitTransaction(); // Need to figure out how to do dirty reads so I can remove this.
echo $question_pid."<br>";
$result = $query->executeRead("SELECT question_pid FROM question where question_pid = '".$question_pid."';");
echo count($result)."<br>";
//if (count($result) === 1) {
$query->createTransaction(); // Need to figure out how to do dirty reads so I can remove this.
foreach($_POST['answer'] as $answer) {
$correctAnswers = 0;
$query->executeCreateUpdateDelete("INSERT INTO answer (question_fid, answer, isCorrect) VALUES ('".$question_pid."','".$answer['answer']."','".$answer['isCorrect']."')");
if ($answer['isCorrect'] === 1) {
$correctAnswers = $correctAnaswers + 1;
if ($correctAnswers > 1){
echo "Failed to insert answers";
$query->rollBackTransaction();
break;
}
}
}
echo "Success";
$query->commitTransaction();
/* } else {
echo "Failed to insert question";
$query->rollBackTransaction();
} */
}
?>
Query.php:
<?php
session_start();
class Query
{
private $host="<censored>";
private $username="<censored>";
private $password="<censored>";
private $db_name="<censored>";
private $pdo;
private $pdo_statement;
private $pdo_exception;
public function executeCreateUpdateDelete($pQuery)
{
$this->pdo_statement = $this->pdo->prepare($pQuery);
return $this->pdo_statement->execute();
}
public function executeRead($pQuery)
{
try
{
$dbh = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
$result = $dbh->query($pQuery);
$dbh = null;
return $result->fetchAll();
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
public function createTransaction()
{
$this->pdo = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
$this->pdo->beginTransaction();
}
public function commitTransaction()
{
$this->pdo->commit();
}
public function rollBackTransaction()
{
$this->pdo->rollBack();
}
public function getLastInsertedId()
{
$this->pdo->lastInsertId();
}
}
?>
When I rewrote my logic to not use a separate query class, I was able to do what I wanted to do. The only thing I've been able to find online about the life cycle of a PHP object is that it begins at the start of a script and ends at the end of a script. Does that imply that my query object is instantiated every time I call one of its methods and garbage collected when that particular method ends? Moving the logic out of that class and into the script caused my logic to work. This is what it looks like now:
ManageQuestions.php:
<?php
session_start();
include('query.php');
echo "Begin <br>";
if (isset($_POST['submit'])) {
echo "manageQuestion <br>";
$host="<censored>";
$username="<censored>";
$password="<censored>";
$db_name="<censored>";
$pdo = new PDO("mysql:host=$host;dbname=$db_name", $username, $password);
$stmt = $pdo->prepare("INSERT INTO question (question) VALUES ('".$_POST['question']."'); ");
$stmt->execute();
$question_pid = $pdo->lastInsertId();
echo $question_pid."<br>";
$stmt = $pdo->query("SELECT question_pid FROM question where question_pid = '".$question_pid."';");
$result = $stmt->fetchAll();
echo count($result)."<br>";
foreach($_POST['answer'] as $answer) {
$correctAnswers = 0;
$stmt = $pdo->prepare("INSERT INTO answer (question_fid, answer, isCorrect) VALUES ('".$question_pid."','".$answer['answer']."','".$answer['isCorrect']."')");
$stmt->execute();
}
echo "Success";
}
?>
Even though this fixed my issue, I don't understand why. If someone could explain that, I would be extremely grateful.
Cheers!
Does that imply that my query object is instantiated every time I call one of its methods and garbage collected when that particular method ends?
No. It's per request, not per method call. So the query object is instantiated every time the script is called and it gets unset (and not necessarily garbage collected) when the script ends.
However you could better manage the resource of the PDO object inside your Query class because you create a new instance (which would mean that it connects again to the database server which is not that cheap). So some lazy loading does not seem bad:
class Query
{
...
/** #var PDO */
private $pdo;
...
private function getPdo() {
if (!$this->pdo) {
$this->pdo = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
}
return $this->pdo;
}
public function executeRead($pQuery)
{
try {
$dbh = $this->getPdo();
$result = $dbh->query($pQuery);
return $result->fetchAll();
} catch (PDOException $e) {
echo $e->getMessage();
}
}
public function createTransaction()
{
$this->getPdo()->beginTransaction();
}
...
Take the following function for example:
private function connect($method, $target = $this->_config->db()) {
try {
if (!($this->_pointer = #fopen($target, $method)))
throw new Exception("Unable to connect to database");
} catch (Exception $e) {
echo $e->getMessage();
}
}
As you can see I inserted the function $this->_config->db() into the parameter $target as it's default value. I understand this is not the correct syntax and am just trying to explain my aim.
$this->_config->db() is a getter function.
Now I know I can use an anonymous function and call it via $target later, but I want $target to also accept direct string values.
How could I give it a default value of the whatever is returned by $this->_config->db() and still be able to overwrite it with a string value?
Why not accept NULL values by default (test with is_null()) and if so call your default function?
You can use is_callable() and is_string().
private function connect($method, $target = NULL) {
if (is_callable($target)) {
// We were passed a function
$stringToUse = $target();
} else if (is_string($target)) {
// We were passed a string
$stringToUse = $target;
} else if ($target === NULL) {
// We were passed nothing
$stringToUse = $this->_config->db();
} else {
// We were passed something that cannot be used
echo "Invalid database target argument";
return;
}
try {
if (!($this->_pointer = #fopen($stringToUse, $method)))
throw new Exception("Unable to connect to database");
} catch (Exception $e) {
echo $e->getMessage();
}
}
I would perform a check to see if a value was passed and call my function in a simple check inside the method:
private function connect($method, $target = '') {
try {
if ($target === '') {
$target = $this->_config->db()
}
if (!($this->_pointer = #fopen($target, $method))) {
throw new Exception("Unable to connect to database");
}
} catch (Exception $e) {
echo $e->getMessage();
}
}
I have a collection of items to save to database, but I want the record to be inserted only if not exists.
I think the most effective way would be to filter the collection before saving. Can Doctrine do it automatically?
Or shall I get all id's of all items in the collection, then query the database for items which do not exists on the list of these id's, then in foreach remove all collection items which we do not need, and finally save collection?
Any better approach suggested?
This is the save function from the Doctrine_Collection class
public function save(Doctrine_Connection $conn = null, $processDiff = true)
{
if ($conn == null) {
$conn = $this->_table->getConnection();
}
try {
$conn->beginInternalTransaction();
$conn->transaction->addCollection($this);
if ($processDiff) {
$this->processDiff();
}
foreach ($this->getData() as $key => $record) {
$record->save($conn);
}
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
return $this;
}
I'm not sure where you are getting your collection from, or if you are manually building it, but you might want to try extending the Doctrine_Collection class and overloading the save function like this
<?php
class My_Collection extends Doctrine Collection
{
public function save(Doctrine_Connection $conn = null, $processDiff = true, $createOnly = true)
{
if ($conn == null) {
$conn = $this->_table->getConnection();
}
try {
$conn->beginInternalTransaction();
$conn->transaction->addCollection($this);
if ($processDiff) {
$this->processDiff();
}
foreach ($this->getData() as $key => $record) {
if($createOnly)
{
if ($record->exists())
{
$record->save($conn);
}
}else{
$record->save($conn);
}
}
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
return $this;
}
}
I don't know anything about Doctrine, but the MySQL REPLACE query does just what you want - updates existing rows & creates new rows if no primary key matches were found.