ZF2 Redis: how to set expiration time for a key - php

I have to set up a Redis on a server to store information from Zend Framework 2.
For now, I can store information, but I can not to give them an expiration time for they naturally renew themselves after a while.
I have not found some documentations about this step and it seems to me rather obscure.
My code:
page: config/autoload/cache.global.php
return array(
'caches' => array(
'redis' => array (
'adapter' => array (
'name' => 'redis',
'lifetime' => 60, //doesn't work
'options' => array (
'server' => array (
'host' => 'x.x.x.x',
'port' => x
),
'ttl' => 10, // seems to have no effect
'namespace' => 'mycache',
),
),
)
)
);
in Controller :
..
use Zend\Cache\StorageFactory;
..
$redis = StorageFactory::factory ($this->getServiceLocator ()
->get ('config')['caches']['redis']);
if ($redis->hasItem ('test')) {
var_dump($redis->getItem ('test'));
$redis->removeItem('test');
} else {
$redis->addItem('test', 'testtest');
}
..
I tried several methods, but everytime, the result is the same, no expiration information appears in Redis :
127.0.0.1:6379> get mycache:test
"testtest"
127.0.0.1:6379> ttl mycache:test
(integer) -1
Thanks for your help!

You can also try this:
$redis = $this->getServiceLocator()->get('Cache\RedisFactory');
$redis->getOptions()->setTtl(10);
$redis->setItem('test', 'Custom Value');
So there is no need to set it globaly in factory.
This work for me :)

Take a look at my redis factory bellow:
<?php
namespace Application\Service\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Cache\Storage\Adapter\RedisOptions;
use Zend\Cache\Storage\Adapter\Redis;
class RedisCacheFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('Config');
$config = $config['redis'];
$options = new RedisOptions();
$options->setServer(
[
'host' => $config["host"],
'port' => $config["port"],
'timeout' => $config["timeout"]
]
);
$options->setTtl(60);
/**
* This is not required, although it will allow to store anything that can be serialized by PHP in Redis
*/
$options->setLibOptions(
[
\Redis::OPT_SERIALIZER => \Redis::SERIALIZER_PHP
]
);
$redis = new Redis($options);
return $redis;
}
}
As you can see from the example, the TTL is set to 60 seconds and its working as expected.

Predis\Client has a "magic call" method command executor for setEx:
$redis->setEx($key, $expireTTL, $value);
This will set key if does not exist with passed value for a custom expiration time.
This will update existing key, resetting expiration time.
Double check as you mentioned to see that everything works as expected:
127.0.0.1:6379>dump your_key
127.0.0.1:6379>ttl your_key
Hope it helps :) !

return array(
'caches' => array(
'redis' => array (
'adapter' => array (
'name' => 'redis',
'options' => array (
'server' => array (
'host' => 'x.x.x.x',
'port' => x
),
'Ttl' => 10, // Starting with capital letter
'namespace' => 'mycache',
),
),
)
)
);

Related

Unable to retrieve a cached piece of information that was saved with Laravel app, but trying to get within a phalcon app

I am trying to save a string with one application to memcached. And then after an http redirect, trying to retrieve that information from a different application on the same server. I am able to save, but retrieving the information is not working.
I have made sure that both applications are not applying a prefix to the cache key. I have run 'memcached-tool localhost:11211 dump | less' on the memcache server to ensure the data exists, which it does. I can access the data from the same application that saves the data. So if I save from the Laravel app, I can retrieve from the laravel app, and vice versa for the phalcon app.
The environment variables used are the same in both apps.
Setup in Laravel (cache.php):
'memcached' => [
'driver' => 'memcached',
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 0,
],
],
],
How I am saving in Laravel:
Cache::put($sha, $viewData, 60);
return redirect("someUrl/{$sha}", 302);
Setup in Phalcon:
$di->setShared('memcached', function(){
// Cache data for one hour
$frontCache = new \Phalcon\Cache\Frontend\Data(
[
'lifetime' => 60,
'prefix' => ''
]
);
// Create the component that will cache 'Data' to a 'Memcached' backend
// Memcached connection settings
return new \Phalcon\Cache\Backend\Libmemcached(
$frontCache,
[
'prefix' => '',
'servers' => [
[
'host' => MEMCACHED_SERVER,
'port' => MEMCACHED_PORT,
'weight' => 0,
]
]
]
);
});
How I am trying to retrieve info in Phalcon:
$cache = $this->di->getShared('memcached');
$info = $cache->get($cacheKey);
Here is the output of the $cache var from xdebug:
Phalcon\Cache\Backend\Libmemcached::__set_state(array(
'_frontend' =>
Phalcon\Cache\Frontend\Data::__set_state(array(
'_frontendOptions' =>
array (
'lifetime' => 60,
'prefix' => '',
),
)),
'_options' =>
array (
'prefix' => '',
'servers' =>
array (
0 =>
array (
'host' => 'servername',
'port' => port,
'weight' => 0,
),
),
'statsKey' => '',
),
'_prefix' => '',
'_lastKey' => '38404efbc4fb72ca9cd2963d1e811e95fe76960b',
'_lastLifetime' => NULL,
'_fresh' => false,
'_started' => false,
'_memcache' =>
Memcached::__set_state(array(
)),
))
And here is the dump from memcached-tool:
Dumping memcache contents
add 38404efbc4fb72ca9cd2963d1e811e95fe76960b 4 1562346522 144
a:5:{s:3:"zip";s:0:"";s:10:"first_name";s:5:"Clint";s:9:"last_name";s:9:"Broadhead";s:5:"phone";s:0:"";s:5:"email";s:20:"blah#blah.com";}
I expected to be able to save to memcache from any application and then retrieve that cache from anywhere else that has access to the same server. But when I try to retrieve in the phalcon app I receive 'null'. Even though I can see the key/value pair on the server. Thanks in advance for your assistance.
I bypassed using Phalcons built-in backend and frontend classes and just went with using the PHP ext-memcached on the phalcon app.
$di->setShared('memcached', function(){
if( !($this->_memcache instanceof \Memcached) ){
if( !class_exists('Memcached') ){
throw new CacheException('Unable to connect to cache');
}
$this->_memcache = new \Memcached();
$this->_memcache->addServer(MEMCACHE_SERVER, MEMCACHE_PORT)
return $this->_memcache;
}
});
I started to go down the path of phalcons use of '_PHCM', but once the above solution worked I abandoned that research. Not the best, but works well for my temporary situation.
Phalcon uses prefixes for all cache keys by default. For the Libmemcached adapter
For instance the get for the Libmemcached adapter has:
let prefixedKey = this->_prefix . keyName;
let this->_lastKey = prefixedKey;
let cachedContent = memcache->get(prefixedKey);
https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/cache/backend/libmemcached.zep#L160
Just make sure that there are no prefixes by setting the prefix option in your options
$di->setShared('memcached', function(){
// Cache data for one hour
$frontCache = new \Phalcon\Cache\Frontend\Data(
[
'lifetime' => 60,
]
);
// Create the component that will cache 'Data' to a 'Memcached' backend
// Memcached connection settings
return new \Phalcon\Cache\Backend\Libmemcached(
$frontCache,
[
'prefix' => '',
'servers' => [
[
'host' => MEMCACHED_SERVER,
'port' => MEMCACHED_PORT,
'weight' => 0,
]
]
]
);
});
https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/cache/backend.zep#L59
Finally if the above does not work, install a utility that will allow you to query your Memcached instance and see what keys are stored there. Check it before you store data with Laravel and then afterwards. This way you will know whether you check the correct thing or not.
Alternatively you can use good old telnet if you are comfortable with the command line to check the keys.

The "access_key" option must be provided to use fixer.io

For the currency conversion i am using "florianv/laravel-swap": "^1.1" library. Florianv/Laravel-swap.
As Fixer.io has changed its implementation, it is necessary to pass the access_key with the request, and because of that i am getting this error: "InvalidArgumentException: The "access_key" option must be provided to use fixer.io in /var/www/project/project-files/vendor/florianv/exchanger/src/Service/Fixer.php:51".
I registered and got the access_key.
I updated the library using composer and now i can see three constants in the vendor/florianv/exchanger/src/Service/Fixer.php.
const ACCESS_KEY_OPTION = 'access_key';
const LATEST_URL = 'http://data.fixer.io/api/latest?base=%s&access_key=%s';
const HISTORICAL_URL = 'http://data.fixer.io/api/%s?base=%s&access_key=%s';
To pass the access key i tried this:
I have a swap.php in config folder which looks something like this:
return [
'options' => [
'cache_ttl' => 86400, // 24 hours.
'cache_key_prefix' => 'currency_rate'
],
'services' => [
'fixer' => true,
],
'currency_layer' => [
'access_key' => 'asdfas7832mw3nsdfa776as8dfa', // Your app id
'enterprise' => true, // True if your AppId is an enterprise one
],
'cache' => env('CACHE_DRIVER', 'file'),
'http_client' => null,
'request_factory' => null,
'cache_item_pool' => null,
];
This had one more option which was commented, i enabled and passed the access_key in it but it doesn't work.
I also added it in services block below 'fixer => true'.
'currency_layer' => [
'access_key' => 'asdfas7832mw3nsdfa776as8dfa'
]
Also in options block:
'options' => [
'cache_ttl' => 86400, // 24 hours.
'cache_key_prefix' => 'currency_rate',
'access_key'=>'7ca208e9136c5e140d6a14427bf9ed21'
],
I tried with adding access_key in config/services.php file but it also didn't work.
'fixer' => [
'access_key' => 'asdfas7832mw3nsdfa776as8dfa'
],
Even i tried, adding to env file and calling from there, but no success. How do i pass the access_key, can anyone help me on this, what should be the approach.
vendor/florianv/exchanger/src/Service/Fixer.php -> don't touch the constant (that was my own error).
Pass the options-array by creating the Builder:
$options = ['access_key' => 'YourGeneratedAPIKeyAtCurrencyLayer'];
$this->exchangeSwap = (new Builder($options))
->add('fixer', $options )
->build();
I hope I could help ;-)

Yii::app()->user->id sometimes gives empty result

Hosted on Heroku
Yii 1.14
PHP 5.6
A stranger error when Yii::app()->user->id returns the id sometimes but at other times returns empty. The same page load results in this strange behaviour.
I check for this in a parent class called AdminController.php
class AdminController extends CController
{
public $partnerCount;
public $vendorCount;
public $plantationMarkers;
public function init()
{
echo Yii::app()->user->id;
if(empty(Yii::app()->user->id)) {
echo 'User id empty: '. Yii::app()->user->id;
//$this->redirect(Yii::app()->createAbsoluteUrl('admin/auth/login'));
exit;
return false;
}
elseif(!Yii::app()->user->checkAccess(User::PARTNER)) {
$this->layout = 'column1';
$this->render('/auth/not-authorized');
return false;
}
$this->partnerCount = $this->getPartnerCount();
$this->vendorCount = $this->getVendorCount();
$this->plantationMarkers = $this->getPlantationMarkers();
return true;
}
So in this code 'User id empty: ' is echoed in some cases while in other cases I get the id.
The point is with no change in code how can this work at some time while not at other times.
Solved by changing the session to be stored in DB table. I am not sure but there was some issue with Yii storing session on the server under Heroku.
The change is required in the config array under 'components'.
From :
'session' => array(
'class' => 'CHttpSession',
'timeout' => 2400,
'cookieParams' => array(
'httpOnly' => true,
'secure' => false,
),
),
To :
'session' => array(
'timeout' => 2400,
'cookieParams' => array(
'httpOnly' => true,
'secure' => false,
),
'class' => 'CDbHttpSession',
'connectionID' => 'db',
'sessionTableName' => 'lig_session',
),

How to send other paramenters from the IdP to the SP in the POST request?

I have configured an identification provider (IdP) part of a SSO system, using SimpleSAMLphp.
The main sections of my configuration files:
config/config.php
$config = array(
[...]
'enable.saml20-idp' => true,
'enable.shib13-idp' => true,
[...]
);
config/authsources.php
$config = array(
[...]
'*-sql' => array(
'sqlauth:SQL',
'dsn' => 'mysql:host=*.*.*.*;port=*;dbname=*',
'username' => '*',
'password' => '*',
'query' => 'SELECT *
FROM users
WHERE username = *
AND password = *',
),
[...]
);
metadata/saml20-idp-hosted.php
$metadata['__DYNAMIC:1__'] = array(
'host' => '__DEFAULT__',
'privatekey' => '../cert/*.key',
'certificate' => '../cert/*.pem',
'auth' => '*-sql',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
'authproc' => array(
3 => array(
'class' => 'saml:AttributeNameID',
'attribute' => 'uid',
'Format' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
),
),
);
metadata/saml20-idp-remote.php
$metadata['https://www.video2brain.com/shibboleth'] = array(
'AssertionConsumerService' => 'http://*/Shibboleth.sso/SAML2/POST',
'SingleSignOnService' => 'http://*/Shibboleth.sso/SAML2/POST',
'SingleLogoutService' => 'http://*/Shibboleth.sso/SLO/POST',
);
The certificates and metadata were successfully configurated. The SSO works fine.
But the service provider (SP) has requested that the IdP has to pass more info of the logged user. The authentication is passed when the query returns a row, but i can't access to the fields in the SELECT.
Currently, the final POST request that my IdP sent to their SP has the following parameters:
HTTP_SHIB_IDENTITY_PROVIDER=https://*/metadata.php,
HTTP_SHIB_AUTHENTICATION_INSTANT=2015-10-20T09:04:42Z,
HTTP_SHIB_AUTHENTICATION_METHOD=urn:oasis:names:tc:SAML:2.0:ac:classes:Password,
HTTP_SHIB_AUTHNCONTEXT_CLASS=urn:oasis:names:tc:SAML:2.0:ac:classes:Password,
HTTP_EMAIL=*#*.*,
HTTP_PERSISTENT_ID=!https://*/shibboleth-sp!6faa919dda0e40e5e42088bcd9beb639ed4dfa5e
And they want the full name of the user in a new parameter. Something like that:
[...]
HTTP_USER_NAME=FooUserName
I have tried using the "Adding attributes (core:AttributeAdd)" method but doesn't work. Is possible do that? Any doc, resource or example for this will be helpful.
Thanks.
I set the parameter as "givenName" instead of "name", and it works!
In the auth query, I put an alias for the user "name" as "givenName".
In the idp-hosted, in the "authproc" key I used de AttributeMap method to add the "givenName".
I did these things before, but I was trying to use "name" as the final parameter "name", and didn't work until I use "givenName".
Someone could say me why?
The parameter name is no configurable?
May be the SP and the IdP has to configure the same name in both sides?

Is it possible to add a subdomain to Route53 using the AWS PHP SDK?

I am working on a project where we will be creating both subdomains as well as domains in Route53. We are hoping that there is a way to do this programmatically. The SDK for PHP documentation seems a little light, but it appears that createHostedZone can be used to create a domain or subdomain record and that changeResourceRecordSets can be used to create the DNS records necessary. Does anyone have examples of how to actually accomplish this?
Yes, this is possible using the changeResourceRecordSets call, as you already indicated. But it is a bit clumsy since you have to structure it like a batch even if you're changing/creating only one record, and even creations are changes. Here is a full example, without a credentials method:
<?php
// Include the SDK using the Composer autoloader
require 'vendor/autoload.php';
use Aws\Route53\Route53Client;
use Aws\Common\Credentials\Credentials;
$client = Route53Client::factory(array(
'credentials' => $credentials
));
$result = $client->changeResourceRecordSets(array(
// HostedZoneId is required
'HostedZoneId' => 'Z2ABCD1234EFGH',
// ChangeBatch is required
'ChangeBatch' => array(
'Comment' => 'string',
// Changes is required
'Changes' => array(
array(
// Action is required
'Action' => 'CREATE',
// ResourceRecordSet is required
'ResourceRecordSet' => array(
// Name is required
'Name' => 'myserver.mydomain.com.',
// Type is required
'Type' => 'A',
'TTL' => 600,
'ResourceRecords' => array(
array(
// Value is required
'Value' => '12.34.56.78',
),
),
),
),
),
),
));
The documentation of this method can be found here. You'll want to take very careful note of the required fields as well as the possible values for others. For instance, the name field must be a FQDN ending with a dot (.).
Also worth noting: You get no response back from the API after this call by default, i.e. there is no confirmation or transaction id. (Though it definitely gives errors back if something is wrong.) So that means that if you want your code to be bulletproof, you should write a Guzzle response handler AND you may want to wait a few seconds and then run a check that the new/changed record indeed exists.
Hope this helps!
Yes, I done using changeResourceRecordSets method.
<?php
require 'vendor/autoload.php';
use Aws\Route53\Route53Client;
use Aws\Exception\CredentialsException;
use Aws\Route53\Exception\Route53Exception;
//To build connection
try {
$client = Route53Client::factory(array(
'region' => 'string', //eg . us-east-1
'version' => 'date', // eg. latest or 2013-04-01
'credentials' => [
'key' => 'XXXXXXXXXXXXXXXXXXX', // eg. VSDFAJH6KXE7TXXXXXXXXXX
'secret' => 'XXXXXXXXXXXXXXXXXXXXXXX', //eg. XYZrnl/ejPEKyiME4dff45Pds54dfgr5XXXXXX
]
));
} catch (Exception $e) {
echo $e->getMessage();
}
/* Create sub domain */
try {
$dns = 'yourdomainname.com';
$HostedZoneId = 'XXXXXXXXXXXX'; // eg. A4Z9SD7DRE84I ( like 13 digit )
$name = 'test.yourdomainname.com.'; //eg. subdomain name you want to create
$ip = 'XX.XXXX.XX.XXX'; // aws domain Server ip address
$ttl = 300;
$recordType = 'CNAME';
$ResourceRecordsValue = array('Value' => $ip);
$client->changeResourceRecordSets([
'ChangeBatch' => [
'Changes' => [
[
'Action' => 'CREATE',
"ResourceRecordSet" => [
'Name' => $name,
'Type' => $recordType,
'TTL' => $ttl,
'ResourceRecords' => [
$ResourceRecordsValue
]
]
]
]
],
'HostedZoneId' => $HostedZoneId
]);
}
If you get any error please check into server error.log file. If you get error from SDK library then there is might PHP version not supported.
if you run this code from your local machine then you might get "SignatureDoesNotMatch" error then Make sure run this code into same (AWS)server environment.

Categories