How to clear cache in Controller in Symfony3.4? - php

after migration Symfony from 3.3 to 3.4, my function not working (it works before). I have to clear cache in controller, and when I execute command below, function returns error.
exec(sprintf(
"php %s/bin/console cache:clear --env=prod",
$this->getParameter('kernel.project_dir')
));
It returns something like that:
Fatal error: require(): Failed opening required '/[...]/var/cache/prod/ContainerAcrshql/getTwig_ExceptionListenerService.php' (include_path='.:/usr/local/share/pear') in /[...]/var/cache/prod/ContainerAcrshql/appProdProjectContainer.php on line 764 Fatal error: require(): Failed opening required '/[...]/var/cache/prod/ContainerAcrshql/getSwiftmailer_EmailSender_ListenerService.php' (include_path='.:/usr/local/share/pear') in /[...]/var/cache/prod/ContainerAcrshql/appProdProjectContainer.php on line 764
In addition I can tell You, that in dev environment it works properly. Also when project run localy and simulate prod env (in address bar I type app.php after localhost:8000). I haven't other server to check if problem still occured

I'm calling an already implemented Symfony's command that clear or warmup cache (tested on Symfony 4).
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
class CommandController extends AbstractController
{
/**
*
* #Route("/command/cache/clear", name="command_cache_clear")
*/
public function command_cache_clear(KernelInterface $kernel)
{
return $this->do_command($kernel, 'cache:clear');
}
/**
*
* #Route("/command/cache/warmup", name="command_cache_warmup")
*/
public function command_cache_warmup(KernelInterface $kernel)
{
return $this->do_command($kernel, 'cache:warmup');
}
private function do_command($kernel, $command)
{
$env = $kernel->getEnvironment();
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => $command,
'--env' => $env
));
$output = new BufferedOutput();
$application->run($input, $output);
$content = $output->fetch();
return new Response($content);
}
}

You should add a valid permissions to the var/ directory to access to cache files:
chmod ... var/ -R
The user used when accessing from web is www-data

Related

Laravel Dusk - Failed to connect to localhost port 9515: Connection refused

I want to create a test which I will use from Controller so I write:
<?php
namespace App\Http\Controllers\Modules;
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Laravel\Dusk\ElementResolver;
use Exception;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Chrome\ChromeProcess;
class TestController extends Controller {
public function test() {
$process = (new ChromeProcess)->toProcess();
if ($process->isStarted()) {
$process->stop();
}
$process->start();
$options = (new ChromeOptions)->addArguments(['--disable-gpu', '--headless', '--no-sandbox']);
$capabilities = DesiredCapabilities::chrome()
->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = retry(1, function () use ($capabilities) {
return RemoteWebDriver::create('http://localhost:9515', $capabilities, 60000, 60000);
}, 50);
$browser = new Browser($driver, new ElementResolver($driver, ''));
$browser->resize(1920, 1080);
$browser->visit('https://example.com/login')->click('#.btn > form > div.auth-form-body.mt-3 > input.btn.btn-primary.btn-block');
$browser->driver->takeScreenshot(base_path('tests/Browser/screenshots/logged.png'));
}
}
When I run this script using localhost:8000/test I got this message:
Facebook \ WebDriver \ Exception \ WebDriverCurlException Curl error
thrown for http POST to /session with params:
{"desiredCapabilities":{"browserName":"chrome","platform":"ANY","chromeOptions":{"binary":"","args":["--disable-gpu","--headless","--no-sandbox"]}}}
Failed to connect to localhost port 9515: Connection refused
How I can solve this problem?
Current I use WAMP server on Win10 for local testing but then I will move the code on Linux Ubuntu 18.
I can't fully explain it, but this works for me on Windows:
$process = (new ChromeProcess)->toProcess();
if ($process->isStarted()) {
$process->stop();
}
$process->start(null, [
'SystemRoot' => 'C:\\WINDOWS',
'TEMP' => 'C:\Users\<User>\AppData\Local\Temp',
]);
[...]
Replace <User> with the name of your user directory.

Symfony command not reaching execution when run by cron

I've built a command in Symfony 4 which works fine when run from CLI but doesn't execute when run by cron. The console command is being run and no errors are thrown.
I've even thrown one in execute and it does not fail/error out:
public function execute(InputInterface $input, OutputInterface $output): void
{
throw new \Exception('I am doing something');
}
My full command looks like this and is autowired:
<?php declare(strict_types = 1);
namespace CRMInterface\Command\Customer;
use CRMInterface\Service\Customer\CustomerSyncService;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CustomerSyncCommand extends Command
{
const COMMAND_NAME = 'crm:sync:customer';
/**
* #var CustomerSyncService
*/
private $syncService;
/**
* #var LoggerInterface
*/
private $logger;
/**
* #param CustomerSyncService $syncService
* #param LoggerInterface $logger
*/
public function __construct(CustomerSyncService $syncService, LoggerInterface $logger)
{
parent::__construct(self::COMMAND_NAME);
$this->syncService = $syncService;
$this->logger = $logger;
}
protected function configure()
{
$this
->setName(self::COMMAND_NAME)
->setDescription('Processes outstanding portal sync tasks');
}
/**
* #param InputInterface $input
* #param OutputInterface $output
* #return void
*/
public function execute(InputInterface $input, OutputInterface $output): void
{
$this->logger->info('Syncing customers...');
try {
$this->syncService->sync();
$this->logger->info('Customer sync complete.');
} catch (\Exception $e) {
$this->logger->error('Customer sync failed: ' . $e->getMessage());
}
}
}
My cron job is as follows:
*/3 * * * * www-data cd /var/www/html && /usr/local/bin/php bin/console crm:sync:customer --env=prod
This set up works in a Symfony 3 app and a Symfony 2.8 app I have running but not with 4 and it's driving me batty.
My bin/console is as follows - I've taken out the stuff to do with APP_ENV because it was superfluous in my case and was failing due to the lack of env vars in cron.
#!/usr/bin/env php
<?php
use CRMInterface\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Debug\Debug;
set_time_limit(0);
require __DIR__.'/../vendor/autoload.php';
if (!class_exists(Application::class)) {
throw new \RuntimeException('You need to add "symfony/framework-bundle" as a Composer dependency.');
}
$input = new ArgvInput();
$env = $input->getParameterOption(['--env', '-e'], $_ENV['APP_ENV'] ?? 'dev');
$debug = ($_ENV['APP_DEBUG'] ?? ('prod' !== $env)) && !$input->hasParameterOption(['--no-debug', '']);
if ($debug) {
umask(0000);
if (class_exists(Debug::class)) {
Debug::enable();
}
}
$kernel = new Kernel($env, $debug);
$application = new Application($kernel);
$application->run($input);
Can anyone point me in the right direction as to why the command is running but not getting down to execute?
It's almost as if it's just running bin/console without the command... could it be something to do with lazy loading?
For those having similar issues with commands not making execution with no errors, check that cron has access to all the environment variables your application uses.
In this case, there was an exception that was thrown, but caught by the Symfony Application class. Environment variable not found: "ELASTICSEARCH_INDEX".. Unfortunately, it just continued on as if it had run. (May create an issue over at the repo).
For Docker on an Ubuntu based apache container, the solution was fairly simple - add a line in the entrypoint script to write the environment variables into the /etc/environment file:
FROM php:7.2
EXPOSE 80 443
CMD ["sh", "./.docker/run.sh"]
run.sh:
#!/usr/bin/env bash
printenv | grep -v "no_proxy" >> /etc/environment \
&& /etc/init.d/cron start \
&& apache2-foreground
Similar to the previous answer, but make sure you add the cron job to the proper user.
Example: crontab -u www-data -e
Edit the cron jobs by updating or adding
*/3 * * * * /usr/local/bin/php /var/www/html/bin/console crm:sync:customer --env=prod
You can add some testing on the command such as the following
0 * * * * if [ -f /var/www/html/bin/console ];
then /var/www/html/bin/console crm:sync:customer --env=prod >>
/var/log/symfony/crm.log; else echo 'console not found'
/var/log/symfony/crm.log; fi >/dev/null 2>&1

Soap and laravel don't work together

I'm trying to connect my client soap to my serve soap without sucess on Laravel. My SO is ubuntu 16.
I've searched on google and here on stackoverflow and I didn't find anything.
This is my route:
Route::get('/server.wsdl','SOAP\WsdlController#index');
Route::any('/server','SOAP\ServidorController#server');
Route::get('/client','SOAP\ClientController#client');
This is my ClientController:
use Zend\Soap\Client;
public function client(){
$client = new Client('http://localhost:8080/server.wsdl',[
'soap_version'=> SOAP_1_1
]);
$array = array(
"num1" => 10,
"num2" => 10
);
print_r($client->sum($array));
}
This is my ServidorController:
use Zend\Soap\Server;
class ServidorController extends Controller
{
public $serverUrl = "http://localhost:8080/server";
public function server()
{
$soap = new Server($this->serverUrl . '.wsdl');
$soap->setClass('App\Http\Controllers\Classes\Hello');
$soap->handle();
}
}
This is my WsdlController:
use Zend\Soap\AutoDiscover;
class WsdlController extends Controller
{
public $serverUrl = "http://localhost:8080/server";
public function index()
{
$soapAutoDiscover = new AutoDiscover(new
\Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence());
$soapAutoDiscover->setBindingStyle(array('style' => 'document'));
$soapAutoDiscover->setOperationBodyStyle(array('use' =>
'literal'));
$soapAutoDiscover->setClass('App\Http\Controllers\Classes\Hello');
$soapAutoDiscover->setUri($this->serverUrl);
$soapAutoDiscover->handle();
}
}
Finally this is my class Hello:
class Hello
{
/**
* #param int $num1
* #param int $num2
* #return int
*/
function soma($num1, $num2)
{
return $num1 + $num2;
}
}
My framework shows this error:
SoapFault
Too few arguments to function App\Http\Controllers\Classes\Hello::soma(), 1 passed and exactly 2 expected
But in the function's declaration I putted the 2 arguments. What's happened whith this?
Ah, I take it soma is actually sum, and you translated before posting?
Look here:
$client->sum($array);
You pass one argument, an array, whereas your class expects two arguments.
Instead:
$client->sum($array['num1'], $array['num2']);
This were my errors: I was running the command on terminal 1 "php artisan serve" and in the terminal 2, I was running "php artisan serve --port:8080" and as you can see bellow, the $client was pointing to 'http://localhost:8080/server.wsdl'.
$client = new Client('http://localhost:8080/server.wsdl',[
'soap_version'=> SOAP_1_1
]);
I've solved my ask when I installed, start and setting the apache2 server on ubuntu 16. I've pointed the default folder to my laravel aplication and it works fine.

FOSHttpCache with Symfony reverse proxy

So I'm trying to configure the FOSHttpCacheBundle (latest release ^2.0) with the default Symfony HttpCache.
Here is the content of my AppCache.php according to the docs:
<?php
use FOS\HttpCache\SymfonyCache\CacheInvalidation;
use FOS\HttpCache\SymfonyCache\EventDispatchingHttpCache;
use FOS\HttpCache\SymfonyCache\DebugListener;
use FOS\HttpCache\SymfonyCache\CustomTtlListener;
use FOS\HttpCache\SymfonyCache\PurgeListener;
use FOS\HttpCache\SymfonyCache\RefreshListener;
use FOS\HttpCache\SymfonyCache\UserContextListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\HttpCache\StoreInterface;
use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
class AppCache extends HttpCache implements CacheInvalidation
{
use EventDispatchingHttpCache;
/**
* Overwrite constructor to register event listeners for FOSHttpCache.
*/
public function __construct(
HttpKernelInterface $kernel,
StoreInterface $store,
SurrogateInterface $surrogate = null,
array $options = []
) {
parent::__construct($kernel, $store, $surrogate, $options);
$this->addSubscriber(new CustomTtlListener());
$this->addSubscriber(new PurgeListener());
$this->addSubscriber(new RefreshListener());
$this->addSubscriber(new UserContextListener());
if (isset($options['debug']) && $options['debug']) {
$this->addSubscriber(new DebugListener());
}
}
/**
* Made public to allow event listeners to do refresh operations.
*
* {#inheritDoc}
*/
public function fetch(Request $request, $catch = false)
{
return parent::fetch($request, $catch);
}
}
Now according to the Symfony docs, enabling the caching proxy is just a matter of setting the app.php file as follow (uncommenting that last line):
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel = new AppCache($kernel);
Which gives me a nice:
PHP message: PHP Catchable fatal error: Argument 2 passed to
Symfony\Component\HttpKernel\HttpCache\HttpCache::__construct() must
be an instance of
Symfony\Component\HttpKernel\HttpCache\StoreInterface, none given,
called in /php/api/current/web/app.php on line 11 and defined in
/php/api/current/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
on line 78
Which makes perfect sense given the HttpCache class constructor.
So the question is, is it the doc that's not up to date, or is it just me missing something really obvious?
use Symfony\Component\HttpKernel\HttpCache\Store;
$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();
$store = new Store('app/cache');
$kernel = new AppCache($kernel, $store);

Laravel 5 console (artisan) command unit tests

I am migrating my Laravel 4.2 app to 5.1 (starting with 5.0) and am a lot of trouble with my console command unit tests. I have artisan commands for which I need to test the produced console output, proper question/response handling and interactions with other services (using mocks). For all its merits, the Laravel doc is unfortunately silent with regards to testing console commands.
I finally found a way to create those tests, but it feels like a hack with those setLaravel and setApplication calls.
Is there a better way to do this? I wish I could add my mock instances to the Laravel IoC container and let it create the commands to test with everything properly set. I'm afraid my unit tests will break easily with newer Laravel versions.
Here's my unit test:
Use statements:
use Mockery as m;
use App\Console\Commands\AddClientCommand;
use Symfony\Component\Console\Tester\CommandTester;
Setup
public function setUp() {
parent::setUp();
$this->store = m::mock('App\Services\Store');
$this->command = new AddClientCommand($this->store);
// Taken from laravel/framework artisan command unit tests
// (e.g. tests/Database/DatabaseMigrationRollbackCommandTest.php)
$this->command->setLaravel($this->app->make('Illuminate\Contracts\Foundation\Application'));
// Required to provide input to command questions (provides command->getHelper())
// Taken from ??? when I first built my command tests in Laravel 4.2
$this->command->setApplication($this->app->make('Symfony\Component\Console\Application'));
}
Input provided as command arguments. Checks console output
public function testReadCommandOutput() {
$commandTester = new CommandTester($this->command);
$result = $commandTester->execute([
'--client-name' => 'New Client',
]);
$this->assertSame(0, $result);
$templatePath = $this->testTemplate;
// Check console output
$this->assertEquals(1, preg_match('/^Client \'New Client\' was added./m', $commandTester->getDisplay()));
}
Input provided by simulated keyboard keys
public function testAnswerQuestions() {
$commandTester = new CommandTester($this->command);
// Simulate keyboard input in console for new client
$inputs = $this->command->getHelper('question');
$inputs->setInputStream($this->getInputStream("New Client\n"));
$result = $commandTester->execute([]);
$this->assertSame(0, $result);
$templatePath = $this->testTemplate;
// Check console output
$this->assertEquals(1, preg_match('/^Client \'New Client\' was added./m', $commandTester->getDisplay()));
}
protected function getInputStream($input) {
$stream = fopen('php://memory', 'r+', false);
fputs($stream, $input);
rewind($stream);
return $stream;
}
updates
This doesn't work in Laravel 5.1 #11946
I have done this before as follows - my console command returns a json response:
public function getConsoleResponse()
{
$kernel = $this->app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArrayInput([
'command' => 'test:command', // put your command name here
]),
$output = new Symfony\Component\Console\Output\BufferedOutput
);
return json_decode($output->fetch(), true);
}
So if you want to put this in it's own command tester class, or as a function within TestCase etc... up to you.
use Illuminate\Support\Facades\Artisan;
use Symfony\Component\Console\Output\BufferedOutput;
$output = new BufferedOutput();
Artisan::call('passport:client', [
'--password' => true,
'--name' => 'Temp Client',
'--no-interaction' => true,
], $output);
$stringOutput = $output->fetch();

Categories