Symfony2 command within an asynchronous subprocess - php

I am new on Symfony2 and I got blocked when trying to run an asynchronous command like this:
class MyCommand extends ContainerAwareCommand{
protected function configure()
{
$this
->setName('my:command')
->setDescription('My command')
->addArgument(
'country',
InputArgument::REQUIRED,
'Which country?'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$country = $input->getArgument('country');
// Obtain the doctrine manager
$dm = $this->getContainer()->get('doctrine_mongodb.odm.document_manager');
$users = $dm->getRepository('MyBundle:User')
->findBy( array('country'=>$country));
}}
That works perfectly when I call it from my command line:
php app/console my:command uk
But it doesn't work when I call it trowh a Symfony2 Process:
$process = new Process("php ../app/console my:command $country");
$process->start();
I get a database error: "[MongoWriteConcernException] 127.0.0.1:27017: not master"
I think that means that the process is not getting my database configuration...
I just want to run an asynchronous process, is there other way to do it?
Maybe a way to call the Application Command that do not require the answer to keep going ?
Maybe I need to use injection?
PS: My current command is just a test, at the end it should be an 'expensive' operation...

Well, I found out what happened...
I use multiple environments: DEV, TEST and PROD.
And I also use differents servers.
So the DEV environment is my own machine with a simple mongodb configuration.
But the TEST environment is on other server with a replica set configuration...
Now the error get full sense: "[MongoWriteConcernException] 127.0.0.1:27017: not master"
To solve it, I've just added the environment parameter (--env=) to the process and everything worked like a charm:
$process = new Process("php ../app/console my:command $country --env=test");
Actually, to get the correct environment I use this:
$this->get('kernel')->getEnvironment();
Which let's my code as follows:
$process = new Process("php ../app/console my:command $country --env=".$this->get('kernel')->getEnvironment());
Maybe is not a beautifull way to do it, but it works for me :)

Disclamer: This might be a bit overkill for what you're trying to do :)
I would choose an opposite way to do it: pthreads
First, quick examination of StackOverflow showed me a really nice example of using pthreads: Multi-threading is possible in php
Then, knowing that you could invoke your command from another command:
http://www.craftitonline.com/2011/06/calling-commands-within-commands-in-symfony2/
... lets you piece all the parts. It's a bit complicated but it does the job.

In case you want to execute your code completely async in Symfony2/3 there is AsyncServiceCallBundle for that.
You should just call it like this:
$this->get('krlove.async')->call('your_service_id', 'method_name', [$arg1, $arg2]);
Internally it uses this approach to run your code in background.

Related

Console.Terminate event on a specific command

I just discovered that it exists a ConsoleEvents::TEMINATE event in Symfony.
I want to use it to execute some additional process after the command execution (and not delaying the command).
But the fact is that i want to execute some process when a specific command is finish, not for all the commands (because i think that consoleevent.terminate is fired for all the commands.
I really don't know how to do that.
Regards.
You can access instance of the command from ConsoleTerminateEvent
It's almost copy paste from documentation of Console component. with full symfony registering listener looks a little different but you should get the idea.
$dispatcher->addListener(
ConsoleEvents::TERMINATE,
function(ConsoleTerminateEvent $event) {
$command = $event->getCommand();
// if it's not the command you want
if (!$command instanceof YourDesiredCommand) {
return;
}
// put your logic here
}
);

How to get currently used Artisan console command name in Laravel 5?

Problem / What I've tried:
Getting the currently used controller and action in Laravel 5 is easy (but not as easy as it should be), however I'm stuck with getting the currently used artisan console command.
To fetch the controller name I do this:
$route = Route::getRoutes()->match(Request::capture());
$listAction = explode('\\', $route->getActionName());
$rawAction = end($listAction);
// controller name and action in a simple array
$controllerAndAction = explode('#', $rawAction);
But when calling from a console action, it always returns the default index controller's name ("IndexController" or so in Laravel). Does anybody know how to make this ?
By the way I've also worked throught Request::capture() but this still gives no info about the command.
The simplest way is to just to look at the arguments specified on the command line:
if (array_get(request()->server(), 'argv.1') === 'cache:clear') {
// do things
}
Yes, you can use $_SERVER directly, but I like to use the helper functions or the Facades, as those will give you the current data.
I go from the assumption that - during unit tests - the superglobals might not always reflect the currently tested request.
By the way: Obviously can also do array_get(request()->server('argv'), '1') or something alike. (request()->server('argv.1') doesnt work at this point). Or use \Request::server(). Depends on what you like most.
As per the Symfony\Component\Console\Command\Command class, the method to return the name of the command (eg. my:command) is:
$this->getName();
You should use it from within an Artisan command extending Illuminate\Console\Command (default on Artisan commands).
Remember that it will return only the command name and not the available parameters (eg. for the command signature my:command {--with-params=} it will only return my:command).
Reflection might be of help? Try this:
$var = new \ReflectionClass($this);
dd($var);

How to get the current console command in Laravel

I can see if a script is running in the console with the App::runningInConsole() command, but I would like to know (for audit logging purposes) which command has been run from console.
To add some context - I need to log whenever a system or user accesses a certain type of data. For users, that's pretty simple - I can use Auth::user() and get their IP address from Request, but for console commands, it's a little more difficult.
I can figure out if a console command is running, which is good, but I need to be able to record which console command is running.
I do not see a way to get that information directly Laravel. I'm also not sure how that would be possible, as you can have one command executing other commands or even creating a new application instance like some test tools do.
There is however a way to achieve that using plain PHP. You can check the server/environment variables to identify how application was executed.
Have a look at
dd($_SERVER['argv']);
If you run a command this should give you the command name in $_SERVER['argv'][1]
You can find more information here: http://php.net/manual/en/reserved.variables.argv.php
To get console command together with arguments you should use something like that:
$command = \Request::server('argv', null);
if (is_array($command)) {
$command = implode(' ', $command);
}
In Laravel you can use \Request:server to get $_SERVER variables.
The problem with $SERVER['arg'] is that it doesnt work when you execute the command on the GUI.
I fixed the problem adding the next code on the command.
private function getAttributes()
{
$arguments = $this->arguments();
unset($arguments[0]);
$arguments = collect($arguments)->implode(' ');
$options = collect($this->options())->filter(function($item) {
return $item;
})->map(function($item, $key) {
$return = '--'.$key;
if($item!==true) {
$return .= '='.$item;
}
return $return;
})->implode(' ');
return $arguments.' '.$options;
}
and you get it calling $this->getAttributes() and you will get all the command with the attributes

how to run phpunit from code?

I want to use a local installation of PHPUnit (via composer) to run my tests and display it on screen (acessing /admin/tests for instance). But the only way to run tests I found in the documentation was the command line tool.
Bellow is an hypothetical example of what I'm looking for:
$session = new PHPUnit_TestSession('path/to/folder');
$results = $session->runAll();
echo $results->failuresCount();
// other hipotetical $result->methods...
// maybe $results->dump()
This may be an overkill but you are in for a treat: https://github.com/NSinopoli/VisualPHPUnit :)
EDIT Here is a rudimentary use of PHPUnit using the TextUI_TestRunner
// make sure you have PHPUnit on your path
require_once "PHPUnit/Framework/TestSuite.php";
require_once "PHPUnit/TextUI/TestRunner.php";
$suite = new PHPUnit_Framework_TestSuite();
$suite->addTestSuite('YourTestCase');
// run the test suite with TextUI_TestRunner
PHPUnit_TextUI_TestRunner::run($suite);
The YourTestCase class is a subclass of PHPUnit_Framework_TestCase, which you can read more on how to write at the official website: http://www.phpunit.de/manual/3.2/en/writing-tests-for-phpunit.html
However, I'd also recommend getting a copy of this book: http://www.amazon.com/Advanced-PHP-Programming-George-Schlossnagle/dp/0672325616 The author teaches you quite a few cool tricks, including autoloading tests, etc.

How do you run raw redis commands in PHP?

I have been using predis to try to figure out how to run raw redis commands, but I am having trouble. The documentation for predis is extremely outdated. It says that there is a method called "rawCommand()" which will allow a user to run raw Redis commands, but I found a changelog that says it is no longer supported:
https://github.com/nrk/predis/blame/master/CHANGELOG
Does anyone have any hints of how I can run raw redis commands?
Here you go. This has worked perfectly for me, and I did not know it even had that function
$cmdSet = $redis->createCommand('set');
$cmdSet->setArgumentsArray(array('library', 'predis'));
$cmdSetReply = $redis->executeCommand($cmdSet);
He has a wiki page on this. Look for sending commands.
I'm actually guessing here, but let's pretend for a while I did not say that aloud.
Check out the function writeCommand() in lib/Predis/Network/StreamConnection.php on line 176 and use it through SimpleDebuggableConnection in examples/SimpleDebuggableConnection.php. You still have to define new commands that are not already defined in lib/predis/commands, as mentioned in the wiki that #Colum referred to.
If you're really feeling adventurous, change the protected method writeBytes() in StreamConnection on line 96 to public. That should enable you to feed it pure redis with
$redis->getConnection()->writeBytes("*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n")
Unfortunately, publicizing the writeBytes() seems to go in to the direction of http://en.wikipedia.org/wiki/Object_orgy anti-pattern.
Good luck!
It's easy!
Take this class: RedisServer
and write:
$redis = new \Jamm\Memory\RedisServer();
$redis->send_command('set','key',5); //here any raw command
PHPRedis
on phpredis extension you can use following:
$redis->rawCommand("count", "a:", 10, "a:");
PRedis
on phpredis you can use following:
$redis->executeRaw(["count", "a:", 10, "a:"]);
PRedis - more complicated way
on predis you can do like this:
<?php
require 'Predis/Autoloader.php';
Predis\Autoloader::register();
class PRredisCOUNT extends Predis\Command\Command {
function getId(){
return 'COUNT';
}
}
class PRredisSUM extends Predis\Command\Command {
function getId(){
return 'SUM';
}
}
function registerHM4Commands($redis){
$redis->getProfile()->defineCommand("count" , "PRredisCOUNT" );
$redis->getProfile()->defineCommand("sum" , "PRredisSUM" );
}
$redis = new Predis\Client("127.0.0.1:2000");
registerHM4Commands($redis);
// after that you can do:
$redis->count($key, $page, $prefix);

Categories