Symfony2 Console Output without OutputInterface - symfony

I am trying to print some Information to the Console in a Symfony Console Command. Regularly you would do something like this:
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
if ($name) {
$text = 'Hello '.$name;
} else {
$text = 'Hello';
}
if ($input->getOption('yell')) {
$text = strtoupper($text);
}
$output->writeln($text);
}
For the full Code of the Example - Symfony Documentation
Unfortunately I can't access the OutputInterface. Is it possible to print a Message to the Console?
Unfortunately I can't pass the OutputInterface to the Class where I want to print some Output.

Understanding the matter of ponctual debugging, you can always print debug messages with echo or var_dump
If you plan to use a command without Symfony's application with global debug messages, here's a way to do this.
Symfony offers 3 different OutputInterfaces
NullOutput - Will result in no output at all and keep the command quiet
ConsoleOutput - Will result in console messages
StreamOutput - Will result in printing messages into a given stream
Debugging to a file
Doing such, whenever you call $output->writeln() in your command, it will write a new line in /path/to/debug/file.log
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Input\ArrayInput;
use Acme\FooBundle\Command\MyCommand;
$params = array();
$input = new ArrayInput($params);
$file = '/path/to/debug/file.log';
$handle = fopen($file, 'w+');
$output = new StreamOutput($handle);
$command = new MyCommand;
$command->run($input, $output);
fclose($handle);
Debugging in the console
It is quietly the same process, except that you use ConsoleOutput instead
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Input\ArrayInput;
use Acme\FooBundle\Command\MyCommand;
$params = array();
$input = new ArrayInput($params);
$output = new ConsoleOutput();
$command = new MyCommand;
$command->run($input, $output);
No debugging
No message will be printed
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Input\ArrayInput;
use Acme\FooBundle\Command\MyCommand;
$params = array();
$input = new ArrayInput($params);
$output = new NullOutput();
$command = new MyCommand;
$command->run($input, $output);

Look at JMSAopBundle https://github.com/schmittjoh/JMSAopBundle and check out this great article http://php-and-symfony.matthiasnoback.nl/2013/07/symfony2-rich-console-command-output-using-aop/

Related

How to see error $application->run($input, $output);

i create a controller to update database in symfony because i can't use command line
/**
* #Route("admin/database/update", name="adyax_database")
*/
public function refreshdatabaseRoutes()
{
ini_set('memory_limit', '-1');
ini_set('max_execution_time', 300);
$kernel = $this->container->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
'command' => 'doctrine:schema:update --force',
]);
$output = new BufferedOutput();
$application->run($input, $output);
return $this->redirectToRoute('homepage');
}
i think it don't work but no error given. How i can understand if some error is given ??
First of all, if you want to get command's result, you should use $output variable. You can get the output content with $output->fetch().
Anyway, you've done a mistake in your $input. In command array's element there should be only command's name, so it's just doctrine:schema:update. Any parameters should be passed as separate elements of this array. If the parameter doesn't take any value (like --force), simply set true as the value.
So in the end you should be fine with:
$input = new ArrayInput([
'command' => 'doctrine:schema:update',
'--force' => true,
]);

GuzzleHttp Client class not found in symfony2

I have loaded in GuzzleHttp from
http://docs.guzzlephp.org/en/5.3/quickstart.html
and have the
use GuzzleHttp\Client;
When i call this action...
public function googlevolumeAction(Request $request)
{
$data = $request->request->all();
$searchStr = $data['search'];
$client = new Client();
$req = $client->request('GET', 'https://www.googleapis.com/books/v1/volumes?q=intitle:' .$searchStr, []);
$decode = json_decode($req->getBody());
$total = $decode->totalItems;
$search = null;
if ($total != 0) {
$search = $decode->items;
}
return $this->render('BloggerBlogBundle:Page:googlevolume.html.twig',
['items' => $search]);
}
I get this error...
Attempted to load class "Client" from namespace "GuzzleHttp".
Did you forget a "use" statement for e.g. "Guzzle\Http\Client",
"Guzzle\Service\Client", "Symfony\Component\BrowserKit\Client",
"Symfony\Component\HttpKernel\Client" or
"Symfony\Bundle\FrameworkBundle\Client"?
Any ideas why?
thanks
Looks like you have a different version of guzzle installed than the docs you are looking at. From the error message you got it seems that if you change your use statement to:
use Guzzle\Http\Client;
It should work.

symfony output in kernel.php

in app.php
$kernel = new AppKernel('prod', false);
in Kernel.php
public function __construct($environment, $debug)
{
print('test');
$this->environment = $environment;
$this->debug = (bool) $debug;
$this->rootDir = $this->getRootDir();
$this->name = $this->getName();
if ($this->debug) {
$this->startTime = microtime(true);
}
$defClass = new \ReflectionMethod($this, 'init');
$defClass = $defClass->getDeclaringClass()->name;
if (__CLASS__ !== $defClass) {
trigger_error(sprintf('Calling the %s::init() method is deprecated since version 2.3 and will be removed in 3.0. Move your logic to the constructor method instead.', $defClass), E_USER_DEPRECATED);
$this->init();
}
}
I want to output this code at the browser for learning purposes . I guess it's buffered, but where can I switch it off and output my print at the browser? Or is there a better way through the console?
Not positive I understand the question but if you look in web/app.php then you will see:
$loader = require_once __DIR__.'/../app/bootstrap.php.cache';
The bootstrap.php.cache file is where the cached (buffered?) copy of the Kernel object lives. You can either edit the cache file and add your print statements or just not include the cache file:
$loader = require_once __DIR__.'/../app/autoload.php';

How can I run symfony 2 run command from controller

I'm wondering how can I run Symfony 2 command from browser query or from controller.
Its because I don't have any possibility on hosting to run it and every cron jobs are setted by admin.
I don't even have enabled exec() function so when I want to test it, I must copy all content from command to some testing controller and this is not best solution.
See official documentation on this issue for newer versions of Symfony
You don't need services for command execution from controller and, I think, it is better to call command via run method and not via console string input, however official docs suggest you to call command via it's alias. Also, see this answer. Tested on Symfony 2.1-2.6.
Your command class must extend ContainerAwareCommand
// Your command
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
class MyCommand extends ContainerAwareCommand {
// …
}
// Your controller
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
class SomeController extends Controller {
// …
public function myAction()
{
$command = new MyCommand();
$command->setContainer($this->container);
$input = new ArrayInput(array('some-param' => 10, '--some-option' => true));
$output = new NullOutput();
$resultCode = $command->run($input, $output);
}
}
In most cases you don't need BufferedOutput (from Jbm's answer) and it is enough to check that $resultCode is 0, otherwise there was an error.
Register your command as a service and don't forget to call setContainer
MyCommandService:
class: MyBundle\Command\MyCommand
calls:
- [setContainer, ["#service_container"] ]
In your controller, you'll just have to get this service, and call the execute method with the rights arguments
Set the input with setArgument method:
$input = new Symfony\Component\Console\Input\ArgvInput([]);
$input->setArgument('arg1', 'value');
$output = new Symfony\Component\Console\Output\ConsoleOutput();
Call the run method of the command:
$command = $this->get('MyCommandService');
$command->run($input, $output);
In my environment ( Symony 2.1 ) I had to do some modifications to #Reuven solution to make it work. Here they are:
Service definition - no changes.
In controller:
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
...
public function myAction() {
$command = $this->get('MyCommandService');
$input = new ArgvInput(array('arg1'=> 'value'));
$output = new ConsoleOutput();
$command->run($input, $output);
}
You can just simply create an instance of your command and run it:
/**
* #Route("/run-command")
*/
public function someAction()
{
// Running the command
$command = new YourCommand();
$command->setContainer($this->container);
$input = new ArrayInput(['--your_argument' => true]);
$output = new ConsoleOutput();
$command->run($input, $output);
return new Response();
}
Here's an alternative that lets you execute commands as strings the same way you would on the console (there is no need for defining services with this one).
You can check this bundle's controller to see how it's done with all the details. Here I'm going to summarize it ommiting certain details (such as handling the environment, so here all commands will run in the same environment they are invoked).
If you want to just run commands from the browser, you can use that bundle as it is, but if you want to run commands from an arbitrary controller here is how to do it:
In your controller define a function like this:
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\StringInput;
private function execute($command)
{
$app = new Application($this->get('kernel'));
$app->setAutoExit(false);
$input = new StringInput($command);
$output = new BufferedOutput();
$error = $app->run($input, $output);
if($error != 0)
$msg = "Error: $error";
else
$msg = $output->getBuffer();
return $msg;
}
Then you can invoke it from an action like this:
public function dumpassetsAction()
{
$output = $this->execute('assetic:dump');
return new Response($output);
}
Also, you need to define a class to act as output buffer, because there is none provided by the framework:
use Symfony\Component\Console\Output\Output;
class BufferedOutput extends Output
{
public function doWrite($message, $newline)
{
$this->buffer .= $message. ($newline? PHP_EOL: '');
}
public function getBuffer()
{
return $this->buffer;
}
}
same as #malloc
but
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
...
public function myAction() {
$command = $this->get('MyCommandService');
// $input[0] : command name
// $input[1] : argument1
$input = new ArgvInput(array('my:command', 'arg1'));
$output = new ConsoleOutput();
$command->run($input, $output);
}
If you have to pass arguments (and/or options), then in v2.0.12 (and may be true for later versions), you need to specify InputDefinition first before instantiating an input object.
use // you will need the following
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputDefinition,
Symfony\Component\Console\Input\ArgvInput,
Symfony\Component\Console\Output\NullOutput;
// tell symfony what to expect in the input
$inputDefinition = new InputDefinition(array(
new InputArgument('myArg1', InputArgument::REQUIRED),
new InputArgument('myArg2', InputArgument::REQUIRED),
new InputOption('debug', '0', InputOption::VALUE_OPTIONAL),
));
// then pass the values for arguments to constructor, however make sure
// first param is dummy value (there is an array_shift() in ArgvInput's constructor)
$input = new ArgvInput(
array(
'dummySoInputValidates' => 'dummy',
'myArg2' => 'myValue1',
'myArg2' => 'myValue2'),
$inputDefinition);
$output = new NullOutput();
As a side note, if you are using if you are using getContainer() in your command, then the following function may be handy for your command.php:
/**
* Inject a dependency injection container, this is used when using the
* command as a service
*
*/
function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* Since we are using command as a service, getContainer() is not available
* hence we need to pass the container (via services.yml) and use this function to switch
* between conatiners..
*
*/
public function getcontainer()
{
if (is_object($this->container))
return $this->container;
return parent::getcontainer();
}
You can use this bundle to run Symfony2 commands from controller (http request) and pass options/parameters in URL.
https://github.com/mrafalko/CommandRunnerBundle
If you run a command that need the env option like assetic:dump
$stdout->writeln(sprintf('Dumping all <comment>%s</comment> assets.', $input->getOption('env')));
You have to create a Symfony\Component\Console\Application and set the definition like that:
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\NullOuput;
// Create and run the command of assetic
$app = new Application();
$app->setDefinition(new InputDefinition([
new InputOption('env', '', InputOption::VALUE_OPTIONAL, '', 'prod')
]));
$app->add(new DumpCommand());
/** #var DumpCommand $command */
$command = $app->find('assetic:dump');
$command->setContainer($this->container);
$input = new ArgvInput([
'command' => 'assetic:dump',
'write_to' => $this->assetsDir
]);
$output = new NullOutput();
$command->run($input, $output);
You can't set the option env to the command because it isn't in its definition.

A different service for my Flex app using Zend_Amf

I have an iterator service that works fine already and returns a correctly structured values to my flex application through my Zend Amf server
$contacts = array();
mysql_connect( 'localhost', 'root', 'test' );
mysql_select_db( 'test' );
$res = mysql_query( 'SELECT * FROM contact' );
while( $contact = mysql_fetch_assoc($res) ) {
$contacts []= $contact;
}
return $contacts;
However I would like to adjust this so that I can leverage my MVC structure and achieve the same results.
I have placed an excerpt that can be brought to working condition
$contacts = array();
$table = new Model_DbTable_Contact();
$result = $table->fetchAll();
//Return an array to be consumed by my flex application
foreach ($result as $row)
{
/*do something*/
}
return $contacts;
You'll want to look into ValueObjects. Zend_Amf supports those, and it's a good idea to use that. That way you can have objects that are native to both PHP and Flex.
$server->setClassMap('ContactVO', 'Contact');
Your Flex would then have a class:
[Bindable]
[RemoteClass(alias="Contact")]
public class ContactVO
{
}
Would tell your server that you're going to map your Contact class to ContactVO in Flex.
then you could do:
$data = array();
foreach ($result as $row)
{
$data[] = new Contact($row);
//assuming the Contact constructor parses the array data
}
return $data;
and your Contact objects would get to Flex as ContactVO objects
So here I have a function in the logical model for a database table:
public function fetchAll() {
$resultSet = $this->getDbTable()->fetchAll();
$entries = array();
foreach( $resultSet as $row ) {
$entry = new Model_ClosingData();
$entry->setId($row->id)
->setContractMonth($row->monthId)
->setCommodity($row->commodityId)
->setDate($row->date)
->setPrice($row->price)
->setVolume($row->volume)
->setOutstandingInterest($row->outstandingInterest);
$entries[] = $entry;
}
return $entries;
}

Resources