Symfony 2.8 functional testing - add values to the session array - php

I'm having a controller which reads an integer from the session array in this manner:
if ($session->has('administration'))
{
$id = $session->get('entity_id');
return $this->getDoctrine()->getRepository('AppBundle:SomeEntity')->find($id);
}
Currently I'm writing tests for this controller action but I can't find how to inject this integer in the sessions array. I tried the following:
$this->client = $this->makeClient(true);
$this->client->getContainer()->set('session.storage.options.entity_id', 12);
This, however doesn't work and seems to just ignore the value. Therefore, my question is how to store values in the session array in a functional test environment? (note: I'm using the Liip test bundle)
Extra configs that may have impact
// config_test.yml
liip_functional_test:
cache_sqlite_db: true
authentication:
username: "%test.username%"
password: "%test.password%"
framework:
test: ~
session:
storage_id: session.storage.mock_file
profiler:
collect: false

Symfony as a Mock of the session for tests, as defined here in the doc
use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
use Symfony\Component\HttpFoundation\Session\Session;
$session = new Session(new MockFileSessionStorage());
$session->set('entity_id', 12);

Related

Access Profiler without a request on a PHPUnit Symfony2 project

How can I access the profile on a unit test context where there is no request?
In my case, I'm making tests to a data access layer that uses some other DBAL, doctrine2 in my case, and I want to assure that only a certain number of queries are made for performance issues. Don't want to put http requests in this scenario.
If I just do code below the collectors don't collect anything, and at the last line an exception is throw because getQueryCount it tries to access a data member set to null:
public function testQueryCount() {
/* #var $profile Profiler */
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
$profile = $this->getContainer()->get('profiler');
$profile->enable();
$eventDataAccess = new EventDataAccess($em);
$e = $eventDataAccess->DoSomething(1);
$a = $profile->get('db');
$numMysqlQueries = $a->getQueryCount();
$this->assertCount(1, $numMysqlQueries)
}
I've got the profiler in the config_test.yml with:
doctrine:
dbal:
connections:
default:
logging: true
framework:
profiler:
only_exceptions: false
collect: true

Symfony1.4 visit session of other applications

I recently created a new symfony application on the existing website. Then, in this new application, I want to read the session of old applications(something like login user id). Unfortunately, in each application, the session are completely separate(I mean the symfony session, something like $this->getUser()->getAttribute("userSession")).
I guess the symfony session is implemented using $_SESSION like:
$_SESSION = array("symfonyapp1" => array(....), "symfonyapp2" => array(....));
So I wrote $_SESSION["test"] = "testStr" in the old application and wrote var_dump($_SESSION["test"]);. The screen simply prints "null", so my guess is wrong.
Then I think maybe I can read the configuration of a certain application and then get the user of that application. So I wrote the following code in my new application:
require_once($_SERVER['DOCUMENT_ROOT'].'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
$context = sfContext::createInstance($configuration);
var_dump($context->getUser()->getAttribute("userId"));
Unfortunately again, it prints "null".
I completely have no idea now. Any advice is greatly appreciated
Check session identifiers of your apps. Php's default session id is PHPSESSID and as far as I remember default session identifier for Symfony 1.4 apps is just symfony
You can change Symfony's session identifier by modyfing apps/YOUR_APP_NAME/config/factories.yml file, by setting:
all:
storage:
param:
session_name: PHPSESSID
By doing that your Symfony app will share the same session id as your old app and you will be able to read $_SESSION attributes in Symfony app

How to attach a password encoder to a User Interface in Silex?

So I am trying to create a new Silex application and use the Security bundle included. For simplicities sake I was going to go with the basic password encoding.
Per the Silex documentation http://silex.sensiolabs.org/doc/providers/security.html I have created a custom User Provider. However this user interface does not seem to use the default password encoding.
I can successfully get a password out of
$password = $app['security.encoder.digest']->encodePassword('foo');
However when I use the example
// find the encoder for a UserInterface instance
$encoder = $app['security.encoder_factory']->getEncoder($user);
// compute the encoded password for foo
$password = $encoder->encodePassword('foo', $user->getSalt());
I get the
RuntimeException: No encoder has been configured for account
In symfony2, I would use something like the following
encoders:
somename:
class: Acme\DemoBundle\Entity\User
Acme\DemoBundle\Entity\User: sha512
Acme\DemoBundle\Entity\User: plaintext
Acme\DemoBundle\Entity\User:
algorithm: sha512
encode_as_base64: true
iterations: 5000
Acme\DemoBundle\Entity\User:
id: my.custom.encoder.service.id
But that doesnt seem to be the case here. I can't seem to find any type of setEncoder method so I am a bit stumped.
You need to reconstruct the EncoderFactory to add your custom implementation:
<?php
$app = new Silex\Application();
$app['myapp.encoder.base64'] = new Base64PasswordEncoder();
$app['security.encoder_factory'] = $app->share(function ($app) {
return new EncoderFactory(
array(
'Symfony\Component\Security\Core\User\UserInterface' => $app['security.encoder.digest'],
'MyApp\Model\UserInterface' => $app['myapp.encoder.base64'],
)
);
});
(oh and please, don't use a Base64Encoder() for password ;))
I was able to use the accepted answer to fix my problem, but I couldn't assign it to security.encoder_factory directly, so I'm just sharing what I found to work.
instead of:
$app['security.encoder_factory'] = $app->share(function($app) {
//..see above...//
});
I had to use:
$app->register(new Silex\Provider\SecurityServiceProvider(),array(
'security.encoder_factory' => $app->share(function($app) {
//... same as above ...//
})
));
I'm too new to Silex to know why it didn't work as above for me. My initial guess would be a version difference (question was asked over two years ago). I can assign to security.provider.default before the call to register the module, but I can't seem to assign to security.encoder_factory . I also seem to have to put security.firewalls in the register call.

Moving php sessions to redis. Is is possible not to lose existing session data?

We're considering moving our php session data to redis.
The setup looks simple. Just set the following in php.ini and restart apache. It should be all set.:
session.save_handler = redis
session.save_path = "tcp://host1:6379"
If possible I would like our users not to notice the migration. Is it possible to move the session data to redis without losing any existing session data?
There is no out-of-the-box solution available right now for what you are asking, but writing a custom script for this task can actually be fairly simple.
Esentially, phpredis stores session data in redis as strings with the following key name format: PHPREDIS_SESSION:$sessionid, where $sessionid is the php id of the session, the one retrievable via session_id(). The session data is "encoded" as a php-session serialized variable (which is a slightly different format to the common php serialize/unserialize, see session_encode).
Now that we know this, there are two possibilities to migrate session data stored in files:
Iterate through every session file (the actual path is set at session.save_path in your php.ini), read the data and write it back to redis. The files themselves store the php-session serialized representation of the session data, which means the content can be copied as it is directly to redis, and the filenames have the following pattern: sess_$sessionid, where $sessionid is, you guessed it, the id you'll want to use for your redis keys.
Migrate the data progressively by staying with file based sessions for now, but populating redis in real time as session data is being used, until the amount of sessions stored in redis looks good enough to do the switch. This could be achieved by doing something like:
$redis->set("PHPREDIS_SESSION:".session_id(), session_encode());
Right before each script ends. This method may add a little bit of overhead
depending on the amount of data in session and how session_encode
works.
Just created such a script in bash and added to my repo.
https://github.com/renasboy/php-redis-migrate-sessions
Possible, yes, easy, not so much.
AFAIK, phpredis does not have a migration script, so you'd have to write one yourself. You may want to take a look at Cm_RedisSession's script that does something similar for that redis module.
If you are using symfony, you can use a command like this:
yml configuration:
parameters:
redis_address: "localhost"
project_name : "ACME_"
snc_redis:
clients:
default:
type: predis
alias: default
dsn: redis://%redis_address%
logging: '%kernel.debug%'
session:
type: predis
alias: session
dsn: redis://%redis_address%/1
logging: true
session:
client: session
prefix: '%project_name%PHPREDIS_SESSION'
ttl: 7776000 # 90 days
symfony command:
<?php
// Command: app/console acme:migrate:session:files:to:redis --env=dev
namespace Acme\AppBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Finder\Finder;
class MigrateSessionFilesToRedisCommand extends ContainerAwareCommand {
protected $env;
protected function configure() {
$this->setName('acme:migrate:session:files:to:redis')
->setDescription("Migrate Session Files To Redis")
->setHelp("Migrate Session Files To Redis");
}
protected function execute(InputInterface $input, OutputInterface $output) {
$sessionPath = realpath( sprintf('%s%s', $this->getContainer()->getParameter('kernel.root_dir'), '/sessions') );
$prefix = 'ACME_PHPREDIS_SESSION';
$redis = $this->getContainer()->get('snc_redis.session');
$finder = new Finder();
$finder->files()->in($sessionPath);
foreach ($finder as $file) {
$realPath = $file->getRealpath();
$sessionId = str_replace( 'sess_', '', $file->getRelativePathname() );
$redis->append( sprintf('%s:%s', $prefix, $sessionId) , file_get_contents( $realPath ) );
}
}
}
Note: Replace "ACME" with your Project ID/Name and Set The right Session Path where files are stored.
Here is my super simple script for importing session data to redis:
#!/bin/bash
export REDISCLI_AUTH=my-supper-strong-password-4-redis-server
TTL=$(( 24 * 3600 ))
for i in sess_*; do
ex=$(( $(date +%s) - $(stat -c %Y "$i") + $TTL ))
k="PHPREDIS_SESSION:${i:5}"
v=$(cat "$i")
echo "SET $k of len:${#v} EX $ex"
redis-cli SET "$k" "$v" EX $ex
done
I did not test it thoroughly, so use it caution.

Can I use MongoDB to store sessions in Symfony 2?

Is it possible to use MongoDB to store sessions in Symfony 2? If so, how?
Just because I think the question is not really answered. In Symfony 2.1 is now possible to store session data in MongoDB with the MongoDbSessionHandler which is included in the HttpFoundation component.
Main configuration of config.yml looks like:
session.handler.mongo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler
arguments: [#mongo, %mongo.session.options%]
mongo.connection:
class: MongoDoctrine\MongoDB\Connection
factory_service: doctrine.odm.mongodb.document_manager
factory_method: getConnection
calls:
- [initialize, []]
mongo:
class: Mongo
factory_service: mongo.connection
factory_method: getMongo
mongo.session.options:
database: app_session
collection: session
framework:
session:
handler_id: session.handler.mongo
Read more here: http://blog.servergrove.com/2012/11/05/storing-sessions-in-mongodb-with-symfony2/
I've done this with Mandango, but it should be easily convertable to use eg Doctrine's MongoDB ODM or similar. I made a start, but it hasn't been tested and I'm fairly sure it should be passed something different to a Mongo instance ;-) I've added placeholders or example code where appropriate eg:
public function __construct(\Mongo $con, ...)
which I'm fairly sure will need to change :-)
Code is up on Github at https://github.com/richsage/Symfony2-MongoDB-session-storage - PRs welcome when you get it working!
Essentially, I've extended the NativeSessionStorage class, and adjusted the various methods to handle inserting, retrieving and updating session records in my Mongo database where appropriate. The class needs to be configured as a service, with the appropriate dependencies added in, and then this service is passed to the session configuration. Et voila :-)

Categories