Clear FileSystemCache in Symfony 3.4 - symfony

I use Symfony\Component\Cache\Simple\FilesystemCache;
It works when I $cache->set $cache->get $cache->clear() etc
I don't want to use a custom ttl. I want to clear the cache setted only with console.
But when I do php bin/console cache:clear, it doesn't clear cache I have set before with FilesystemCache.
I have tried to clear every pools with console but it doesn't clear $cache either.

1. Why it happens
Symfony's bin/console cache:clear command clears the cache only from kernel cache dir, which is var/cache/{env} by default.
When you create instance of FilesystemCache, you can provide a path where you want to store your cache as a 3rd parameter. Here's a signature of FilesystemCache constructor
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null)
If you don't provide 3rd parameter it will end up as sys_get_temp_dir().'/symfony-cache', which is /tmp/symfony-cache on Linux.
As you can see it's a different directory and it won't be cleared by cache:clear command.
2. How to fix it
The proper way
You need to create your own data-cache:clear command. It's very simple https://symfony.com/doc/current/console.html
In execute() method of your command you should instantiate your FilesystemCache and call clear() on it. Example:
protected function execute(InputInterface $input, OutputInterface $output)
{
$cache = new FilesystemCache();
$cache->clear();
}
Then you can call php bin/console data-cache:clear from console.
If you decide to switch to some other caching engine in future (Redis, Memcached etc.) you can simply adjust that command to clear that cache.
The wrong way
It will only work if you keep using FilesystemCache and does not
provide fine-grained control of which cache you actually clear.
You can store your cache in kernel.cache_dir by passing a 3rd parameter to FilesystemCache when you instantiate it.
Example:
$cache = new FilesystemCache('', 0, $container->getParameter('kernel.cache_dir').'/data-cache');
or when configured as a service
Symfony\Component\Cache\Simple\FilesystemCache:
arguments:
- ''
- 0
- '%kernel.cache_dir%/data-cache'
This way Symfony's cache:clear command will work for you, but it's not a good idea to store these 2 types of cache in the same place.
If you change some of your project files, you may want to clear only
kernel cache in /var/cache while keeping your data cache intact and
vice versa. That's why I recommend not to use this solution!

It finally works, using AdapterInterface
<?php
namespace Gh\GhBundle\Manager;
use Symfony\Component\Cache\Adapter\AdapterInterface;
class AppManager
{
protected $_rootDir;
protected $_cache;
public function __construct($rootDir, AdapterInterface $cache)
{
$this->_rootDir = $rootDir;
$this->_cache = $cache;
}
/**
*
* Get version of this app
* #return string
*/
public function getVersion()
{
$cache = $this->_cache;
$numVersion = $cache->getItem('stats.num_version');
if (!$numVersion->isHit()) {
$version = !file_exists($this->_rootDir . '/RELEASE.TXT') ? 'dev' : file_get_contents($this->_rootDir . '/RELEASE.TXT');
$numVersion->set($version);
$cache->save($numVersion);
}
return $numVersion->get();
}
/**
*
* Get name of this app
* #return string
*/
public function getName()
{
return 'GH';
}
}

Related

Executing Symfony 4 Custom Command in fixtures

I have a custom command in my symfony project to populate the database with the default data that the application need to work in both dev and prod environments.
For the dev environment I have a fixture script that depends on these default common data.
I'm trying to call my custom Symfony command in the fixture script so that I'm sure to have the required data to properly load my fixtures.
This is my custom command app:db:populate in "pseudo script", just creating a bunch of entities, persit & flush. My custom command works fine when I call it through php bin/console app:db:populate
protected function execute(InputInterface $input, OutputInterface $output)
{
// Creating a bunch of default entities, persist them and flush
$data = new MyDefaultEntity();
// ...
$this->manager->persist($data);
// ...
$this->manager->flush();
}
Then, in my fixture script, I want to call app:db:populate first, because fixtures depends on these data. So I tried to use the Process class to execute my script this way :
public function load(ObjectManager $manager)
{
// Execute the custom command
$cmd = 'php bin/console app:db:populate';
$process = new Process($cmd);
$process->run(function ($type, $buffer) {
if (Process::ERR === $type) {
echo 'ERR > '.$buffer;
} else {
echo 'OUT > '.$buffer;
}
});
// Then load the fixtures !
// ...
}
The custom command seems to execute well until the $this->manager->flush();
I have the following error in my console (Data is obfuscated for the post):
In AbstractMySQLDriver.php line 36:
An exception occurred while executing 'INSERT INTO ....(..., ..., ...) VALUES (?, ?, ?)' with params ["...", "...", "..."]:
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction
I don't know what to do regarding this error ... Why the command is working normally when used through a classic console call and why it is not working in a Process?
So, the short answer is
Quoting Symfony documentation :
You may have the need to execute some function that is only available in a console command. Usually, you should refactor the command and move some logic into a service that can be reused in the controller.
I ended up making a service class that handles all the app:db:populate logic (read a json file and insert basic app entities in the database). Then I call this service in both app:db:populate execute methods and AppFixtures load methods.
Hope this will help someone.

Loading a Symfony parameter in the AppKernel

We need to store our cache and log files outside of the project folder structure. I have set up parameters_prod.yml and parameters_dev.yml that will be build by Bamboo on deployment to different servers/environments.
Is there any way that I can access these parameters in the AppKernal so that I can use them in the getCacheDir() function? This would be the easy way of doing things short of parsing them myself or something.
So the final directory structure should look the same as the default Symfony one, with the exception of the cache and logs. The server team has requested that the cache and logs should be under var/tmp and var/logs. So for an application, the cache would be /var/tmp/symfony/projectName/prod and /var/tmp/symfony/projectName/dev. Logs would follow a similar structure.
So basically the structure would follow the normal Symfony one except /var/www/Symfony/projectName/var/cache becomes /var/tmp/symfony/projectName and /var/www/Symfony/projectName/var/logs becomes /var/logs/symfony/projectName. Note that all these locations here are absolute (and the location of the root of the project may differ slightly, when Bamboo deploys, it will set up the correct paths etc).
One of the strange things is that when I set it up like this, the site actually runs, but I can not see anything under the new cache location (have not started working on the logs side yet). So there has to be cache files somewhere, but a locate doesn't even find them!
NOTE: I have now found that if you run the internal server, this problem doesn't happen. This only happens if you are loading the site under Apache.
The problem with your idea is that a service container and parameters are initialized just moment after the ConfigCache object has been constructed, with a absolute cache path as parameter.
namespace Symfony\Component\HttpKernel;
...
/**
* The Kernel is the heart of the Symfony system.
*
* It manages an environment made of bundles.
*
* #author Fabien Potencier <fabien#symfony.com>
*/
abstract class Kernel implements KernelInterface, TerminableInterface
{
...
/**
* Initializes the service container.
*
* The cached version of the service container is used when fresh, otherwise the
* container is built.
*/
protected function initializeContainer()
{
$class = $this->getContainerClass();
// !!!!!! cache config object construction
$cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);
$fresh = true;
if (!$cache->isFresh()) {
$container = $this->buildContainer();
$container->compile();
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
$fresh = false;
}
require_once $cache->getPath();
$this->container = new $class();
$this->container->set('kernel', $this);
if (!$fresh && $this->container->has('cache_warmer')) {
$this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));
}
}
}
So, you can't have access to your custom defined parameters in a getCacheDir() method.
Could you override the getCacheDir() method?
Let's say that your directory structure looks like this
-home
--symfony_app
--custom_cache_directory
Than method override would look something like this:
public function getCacheDir()
{
return dirname(__DIR__).'/../custom_cache_directory/cache/'.$this->getEnvironment();
}
More info at official docs: http://symfony.com/doc/current/configuration/override_dir_structure.html#override-the-cache-directory
So the answer is to do as Matko suggested, and DON'T use the /var/tmp directory.
So in /appName/app/AppKernel.php I edited the getCacheDir() to make it look more like:
public function getCacheDir()
{
return '/var/symfony/cache/projectName' . '/' . $this->getEnvironment();
}
or whatever path you want to use.
Don't use anything under /tmp or /var/tmp (I'm on RHEL) as these folders do some weird things (/var/tmp seemed to never actually write the cache to disk).

symfony 3 session flashdata not working

use Symfony\Component\HttpFoundation\Session\Session;
...
public function __construct()
{
//echo ini_get('session.auto_start'); die;
$this->session = new Session();
/**
* #Route("/", name="registration_index")
* #Route("/user/registration", name="registration")
* #Method("GET")
*/
public function indexAction()
{
//$this->session->getFlashBag()->add('notice', 'Profile updated');
$errors = $this->session->getFlashBag()->get('notice', array());
print_r($errors);
return $this->render('registration.html.twig', ['errors' => $errors]);
}
/**
* #Route("/user/registration", name="post_registration")
* #Method("POST")
* #return mixed
*/
public function postAction()
{
//$this->session->getFlashBag()->add('errors', 'hahaha');
$this->session->getFlashBag()->add('notice', 'Profile updated');
return $this->redirectToRoute('registration');
}
I will want to display error messages near input fields if there is registration failure. Trying to use session flashdata.
When user goes to postAction method, he is redirected to indexAction. But
print_r($errors);
prints empty array. Why is that? Without redirect - it works ok.
This was not symfony problem, but php server problem. Even code examples from php manual without symfony did not work.
I was running server like this:
php -S 0.0.0.0:8000
and for some reasons the session did not work on it.
When I started using nginx server, session started working.
Is there a reason why you are not using symfony Forms to get this functionality? Symfony Forms are perfect for registration pages.
By the way, if you are doing this in a controller then you don't need the constructor method. Instead get the session from Request->getSession()
I confirm that WebCookie sais in his comment: In the controller please use $this->addFlash() if your need to add a flash message within a controller.
Notice you can also use session service like this: $this->get('session')->getFlashBag()->add()
For further informations Flash messages.

Symfony best practice for data export file location

I am writing a console command which generates data files to be used by external services (for example, a Google feed, inventory feed, etc). Should the location of the generated data files be within the Symfony app? I know they can actually be anywhere, I'm just wondering if there is a standard way to do it.
It's up to you, but it is better to have this path in a parameter. For example you can you have a parameter group related to your command. This allows you to have different configurations depending on the current environment:
parameters:
# /app/config.yml
# #see MyExportCommand.php
my_export_command:
base_path: '/data/ftp/export'
other_command_related_param: true
In your command, get and store those parameters in the initialize function:
// MyExportCommand.php
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->parameters = $this->getContainer()->getParameter('my_export_command');
}
Finally in your execute function, you can use something like this: ($this->fs is an instance of the Symfony2 Filesystem component)
// execute()
// Write the file
$filePath = $this->parameters['base_path']. '/'. $this->fileName;
$this->fs->dumpFile($filePath, $myContent);

Symfony 2 Service Error on unit Testing

I make some functional test with Symfony 2 and phpunit.
But i've some trouble with a Service.
Let me explain.
During my run test, i want to use some service used by the application. So i juste set my setUp function for setting the kernel :
static::$kernel = static::createKernel();
static::$kernel->boot();
$this->objectFactory = static::$kernel->getContainer()->get('some.application.objectfactory');
So i've this and in my function i need to used a service that return an object so i call my service like that
$var = $this->objectFactory->getObject($id);
and obviously in my tearDown function i just :
protected function tearDown()
{
$this->client->restart();
unset($this->client, $this->objectFactory);
}
So my problem is when i run a test i've this message :
Symfony\Component\DependencyInjection\Exception\InactiveScopeException: You cannot create a service ("request") of an inactive scope ("request").
And i can't find a way to solve this.
Did someone have any idea ??
My version of Symfony is 2.2.1 and my version of phpunit is 3.7.19
If someone can help me, i could be very happy.
I'm sorry if my English isn't so good.
EDIT
Maybe it could help someone, in the service i used that : :
$request = $this->container->get('request');
It seems to be the reason why it dosen't work, when i remove it, it doesn't say the error, but they still doesn't work.
EDIT
#Cyprian
According to you have change my code for what i want.
So i just add to my service, in the function that i want, the client (Client web test case), and then inside the function i just add this :
if (isset($client)) {
$request = $client->getRequest();
} else {
$request = $this->container->get('request');
}
So in my function where i call the service i've just this :
public function getObject($id)
{
//Get the service from the kernel
$service = static::$kernel->getContainer()->get('service');
$object = $service->getObject($id, $this->client);
}
and it works fine like this
#nifr
Your idea doesn't work for me, but i think your idea wasn't wrong, they just not works in my case
However Thanks for your help, i'm happy i works now, and i expect that post could help someone else
Try get request from the client, not service container:
$request = $this->client->getRequest();
In that way you can also get kernel and/or container:
$kernel = $this->client->getKernel();
$container = $this->client->getContainer();
One more useful tip: kernel from the client is rebooted between each two requests. So, for example if you pass your mock to client's container and do some request, in next request (after the first one) the container will not contain your mock.
There is no request available in phpUnit as long as you don't construct one.
If you want to test a request. create it like this:
use Symfony\Component\HttpFoundation\Request;
protected $request;
public function setUp()
{
// ...
$this->request = new Request();
// ... modify your request acccording to your needs
}
and add/call a setter in your Service using the request.
$service = $this->kernel->getContainer()->get('your_service')
$service->setRequest($this->request);
or create a Functional Test with WebtestCase.

Resources