Get session outside application (Symfony) - php

I created a php file in \web folder (that's mean outside of the application right?), I set a variable in a session within the application (in a controller action), but I can't get that session outside the application (in the external php file). I got always empty session.
this is the controller's action where I set the session
*/
public function questionnaireConfigAction($id,Request $request)
{
$em=$this->getDoctrine()->getManager();
$questionTypes=$em->getRepository(QuestionType::class)->findAll();
$questionnaire = $em->getRepository(Questionnaire::class)->find($id);
$session = new Session();
$session->set('fileContext',['questionTypes'=>$questionTypes,'questionnaire'=>$questionnaire]);
return $this->render('questionnaires/questionnaireConfig/config.html.twig',
['questionTypes'=>$questionTypes,
'questionnaire'=>$questionnaire]);
}
and here is the external file
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\twigExtentions\filesLoader;
use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
$loader = require __DIR__.'/../app/autoload.php';
$request = Request::createFromGlobals();
$session = new Session();
$session->start();
dump($session->get('fileContext')); /*get nothing here */

[SOLVED]
i find the solution . by using PhpBridgeSessionStorage class
ini_set('session.save_handler', 'files');
ini_set('session.save_path', 'path/to/save file');
session_start();
$session = new Session(new PhpBridgeSessionStorage());
$session->start();
dump($session->get('fileContext'));
here the symfony doc

Related

PHP Unit doesn't detect my test - Symfony - Unit test URL Profile user Page

I already have several files with form or URL tests. however, for tests where user login is needed (I used Symfony's predefined loginUser function), they are not detected. when I use the command "php bin / phpunit" on the terminal, the number of tests has not increased, and these tests are not taken into account. How can I do please?
Here is the code, for example, of the test for the URL of the profile page:
<?php
namespace App\tests\Controller;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ProfilControllerTest extends WebTestCase
{
public function testMyProfil()
{
$client = static::createClient();
$userRepository = static::$container->get(UserRepository::class);
//retrieve the test user (in the fixtures)
$testUser = $userRepository->findOneByEmail('Alex#gmail.com');
//simulate $testUser being logged in
$client->loginUser($testUser);
// test the profile page
$client->request('GET', '/monProfil');
$this -> assertEquals ( 200 , $client -> getResponse () -> getStatusCode ());
}
}
Here is an example of a detected test with PHPUnit, it's also an URL test but with no login required :
<?php
namespace App\tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class AboutControllerTest extends WebTestCase
{
public function testAbout()
{
$client = static::createClient();
//teste the page "about us"
$client->request('GET', '/aboutUs');
$this -> assertEquals ( 200 , $client -> getResponse () -> getStatusCode ());
}
}
When you use the loginUser($user) function, what it does is creates a "browser token" and set it to the session. When you unit test this operation doesn't work.
What you can do is;
$token = new UsernamePasswordToken($user, null, 'main', []);
$client->getContainer()->get('security.token_storage')->setToken($token);
This sets the necessary token for the user and simulates the login process.
The UsernamePasswordToken class is;
Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
For further testing, after setting the token to the client you can get the authorization_checker and check if the user is granted for a specific model.
$auth = $client->getContainer()->get('security.authorization_checker');
$post = new Post();
$this->assertTrue($auth->isGranted(AbstractObjectsVoter::READ, $post));

How to call for a monolog from the index.php?

There is example of using Monolog with PHP-DI (from manual, there are 2 files - index.php and config.php:
<?php
// config.php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
return [
// ...
Psr\Log\LoggerInterface::class => DI\factory(function () {
$logger = new Logger('mylog');
$fileHandler = new StreamHandler('path/to/your.log', Logger::DEBUG);
$fileHandler->setFormatter(new LineFormatter());
$logger->pushHandler($fileHandler);
return $logger;
}),
];
This config.php is using in the below code:
// index.php
use DI\ContainerBuilder;
require __DIR__ . '/../vendor/autoload.php';
$containerBuilder = new ContainerBuilder;
$containerBuilder->addDefinitions(__DIR__ . '/config.php');
$container = $containerBuilder->build();
How could I use it now in index.php? Regularly I use Monolog this way:
$log = new Logger('name');
$log->warning('Foo');
But how to call it with Container?
I was able to do it in simple $container->set(), $container->get() mode. But in this way using Container Builder I can't find a way to do it. Moreover when I make var_dump($container) there are no any logger's signs in it.
You need to replace $log = new Logger('name') with:
$log = $container->get(\Psr\Log\LoggerInterface::class);
See this part of the documentation: http://php-di.org/doc/getting-started.html#3-create-the-objects

HttpFoundation Session stand alone component and forms renderer

(I deleted previous similar subject, because there was too many changed in the code).
I use Symfony\Component\HttpFoundation\Session\Session and Symfony\Component\HttpFoundation\Request but without all framework.
I have simple index
$request = Request::createFromGlobals();
$session = new Session();
if(!$session->isStarted()){
$session->start();
}
$request->setSession($session);
$kernel = new Kernel(new AppContainer());
$response = $kernel->handle($request);
$response->send();
it works well when I use just twig templates.
When I use any class what implements FormRendererInterface it throw me an error.
I expect that by CsrfTokenManager.
This problem doesn't exists when I use $session = new Session(new PhpBridgeSessionStorage()); in index.php. Unfortunately in such case sessions in the next request are empty (this is logic, because I sessions auto start in php.inii is disabled).
Bellow is code what I use in the controller to use form builder.
...
$form = (new LoginForm($this->formBuilder))->getForm($this->generateUrl('login'));
$form->handleRequest($request);
Match::val($form->isSubmitted() && $form->isValid())
->of(
When::equals(true, function($item) use ($form){
$this->commandBus->dispatch(new UserLogin($form->getData()));
}),
When::other(false)
);
...
Thanks for any hints.
SOLUTION
The issues was because to build form I use class FormBuilder what is abstract for others forms and it was provided as service.
There is creating csrf token and to do it I need to session instance.
I could not fond sessions in this class because it was configured in AppContainer.
So finally I had session what was start in the index.php and next try to start in the SessionTokenStorage. It has been throwed an error.
Now session is create in the AppContainer as public service.
I can set the same instance as parameter for others services and also add to the request by $appContainer->get('sessions').
Little bit of code
services.yaml
...
sessions:
class: Symfony\Component\HttpFoundation\Session\Session
arguments: []
public: true
form.builder:
class: Iaso\Web\Component\Form\FormBuilder
arguments: ['#twig','#config.loader', '#sessions']
public: true
index.php
<?php
...
$request = Request::createFromGlobals();
$appContainer = new AppContainer();
$session = $appContainer->get('sessions');
if(!$session->isStarted()){
$session->start();
}
$request->setSession($session);
$kernel = new Kernel($appContainer);
$response = $kernel->handle($request);
$response->send();

Is this data being overwritten by another component?

I'm doing some programming in Silex with the symfony components and I think I have found a bug with the symfony/serializer and the symfony/validator components.
First let me explain what I'm traing to achieve, then let's go to the code.
My objective is to annotate a class with information like serialization directives as well as validation directives. As the reading of these annotations can cost a litle cpu, I like to cache them in memory. For this purpose, I'm using memcache wrapper in the Doctrine/Common/Cache package.
The problem I face is that both the symfony/serializer and the symfony/validator write Metadata to the cache using the class name as key. When they try to retrieve the metadata later, they throw an exception, because the cache has invalid metadata, either an instance of Symfony\Component\Validator\Mapping\ClassMetadata or Symfony\Component\Serializer\Mapping\ClassMetadataInterface.
Following is a reproductible example (sorry if its big, I tried to make as small as possible):
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
class Foo
{
/**
* #var int
* #Assert\NotBlank(message="This field cannot be empty")
*/
private $someProperty;
/**
* #return int
* #Groups({"some_group"})
*/
public function getSomeProperty() {
return $this->someProperty;
}
}
use Doctrine\Common\Annotations\AnnotationReader;
use \Memcache as MemcachePHP;
use Doctrine\Common\Cache\MemcacheCache as MemcacheWrapper;
$loader = require_once __DIR__ . '/../vendor/autoload.php';
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$loader, 'loadClass']);
$memcache = new MemcachePHP();
if (! $memcache->connect('localhost', '11211')) {
throw new \Exception('Unable to connect to memcache server');
}
$cacheDriver = new MemcacheWrapper();
$cacheDriver->setMemcache($memcache);
$app = new \Silex\Application();
$app->register(new Silex\Provider\SerializerServiceProvider());
$app['serializer.normalizers'] = function () use ($app, $cacheDriver) {
$classMetadataFactory = new Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory(
new Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader(new AnnotationReader()), $cacheDriver);
return [new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer($classMetadataFactory) ];
};
$app->register(new Silex\Provider\ValidatorServiceProvider(), [
'validator.mapping.class_metadata_factory' =>
new \Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory(
new \Symfony\Component\Validator\Mapping\Loader\AnnotationLoader(new AnnotationReader()),
new \Symfony\Component\Validator\Mapping\Cache\DoctrineCache($cacheDriver)
)
]);
$app->get('/', function(\Silex\Application $app) {
$foo = new Foo();
$app['validator']->validate($foo);
$json = $app['serializer']->serialize($foo, 'json');
return new \Symfony\Component\HttpFoundation\JsonResponse($json, \Symfony\Component\HttpFoundation\Response::HTTP_OK, [], true);
});
$app->error(function (\Exception $e, \Symfony\Component\HttpFoundation\Request $request, $code) {
return new \Symfony\Component\HttpFoundation\Response('We are sorry, but something went terribly wrong.' . $e->getMessage());
});
$app->run();
After running this example you get fatal errors.
Can anyone confirm that I'm not making a hard mistake here?
Currently my workaround for this is rewrite the DoctrineCache class making use of a namespace for the cache keys. Its working, but I think its ugly.
I think what you need to do is two separate CacheDrivers. See https://github.com/doctrine/cache/blob/master/lib/Doctrine/Common/Cache/CacheProvider.php for how namespaces are used there.
You could:
$validatorCacheDriver = new MemcacheWrapper();
$validatorCacheDriver->setMemcache($memcache);
$validatorCacheDriver->setNamespace('symfony_validator');
$serializerCacheDriver = new MemcacheWrapper();
$serializerCacheDriver->setMemcache($memcache);
$serializerCacheDriver->setNamespace('symfony_serializer');
// note that the two drivers are using the same memcache instance,
// so only one connection will be used.
$app['serializer.normalizers'] = function () use ($app, $serializerCacheDriver) {
$classMetadataFactory = new Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory(
new Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader(new AnnotationReader()), $serializerCacheDriver);
return [new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer($classMetadataFactory) ];
};
$app->register(new Silex\Provider\ValidatorServiceProvider(), [
'validator.mapping.class_metadata_factory' =>
new \Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory(
new \Symfony\Component\Validator\Mapping\Loader\AnnotationLoader(new AnnotationReader()),
new \Symfony\Component\Validator\Mapping\Cache\DoctrineCache($validatorCacheDriver)
)
]);
I've trimmed the code to only show the parts that play some part in the solution. I hope this helps!

Symfony3 Hook Up Annotation Routes

I'm writing my own PHP framework built on top of Symfony components as a learning exercise. I followed the tutorial found at http://symfony.com/doc/current/create_framework/index.html to create my framework.
I'd now like to wire up my routes against my controllers using annotations. I currently have the following code to setup the routing:
// Create the route collection
$routes = new RouteCollection();
$routes->add('home', new Route('/{slug}', [
'slug' => '',
'_controller' => 'Controllers\HomeController::index',
]));
// Create a context using the current request
$context = new RequestContext();
$context->fromRequest($request);
// Create the url matcher
$matcher = new UrlMatcher($routes, $context);
// Try to get a matching route for the request
$request->attributes->add($matcher->match($request->getPathInfo()));
I have come across the following class to load the annotations but I'm not sure how to use it:
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php
I'd appreciate it if someone could help.
Thanks
I've finally managed to get this working. First I changed where I included the autoload.php file to the following:
use Doctrine\Common\Annotations\AnnotationRegistry;
$loader = require __DIR__ . '/../vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
Then I changed the routes collection bit (in the question) to:
$reader = new AnnotationReader();
$locator = new FileLocator();
$annotationLoader = new AnnotatedRouteControllerLoader($reader);
$loader = new AnnotationDirectoryLoader($locator, $annotationLoader);
$routes = $loader->load(__DIR__ . '/../Controllers'); // Path to the app's controllers
Here's the code for the AnnotatedRouteControllerLoader:
class AnnotatedRouteControllerLoader extends AnnotationClassLoader {
protected function configureRoute(Route $route, ReflectionClass $class, ReflectionMethod $method, $annot) {
$route->setDefault('_controller', $class->getName() . '::' . $method->getName());
}
}
This has been taken from https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/Routing/AnnotatedRouteControllerLoader.php. You may wish to modify it to support additional annotations.
I hope this helps.

Categories