2012-05-08 80 views
37

我在想如何從瀏覽器查詢或從控制器運行Symfony 2命令。如何從控制器運行symfony 2運行命令

因爲我沒有任何可能託管運行它,並且每個cron作業都由admin設置。

我甚至沒有啓用exec()函數,所以當我想測試它時,我必須將所有內容從命令複製到某個測試控制器,這不是最好的解決方案。

回答

54

official documentation在這個問題上進行的Symfony的新版本


你不需要從控制器命令執行服務,我想,這最好是通過run方法調用命令,而不是通過控制檯字符串輸入,但是official docs建議您通過別名調用命令。另請參閱this answer。在Symfony 2.1-2.6上測試。

你的命令類必須擴展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); 
    } 
} 

在大多數情況下,你不需要BufferedOutput(從JBM的答案),它是足夠檢查$resultCode is 0,否則出現了錯誤。

+3

+1爲'--some-option'示例 – gondo

+0

我沒有在2.4中使用'setContainer'方法 – RedactedProfile

+0

@DJDarkViper查看更新後的答案。 – Dmitriy

55

註冊您的命令,作爲一個服務,不要忘了打電話給setContainer

MyCommandService: 
    class: MyBundle\Command\MyCommand 
    calls: 
     - [setContainer, ["@service_container"] ] 

在你的控制器,你只需要得到這個服務,並調用與權利參數的execute方法

設定輸入與setArgument方法:

$input = new Symfony\Component\Console\Input\ArgvInput(); 
$input->setArgument('arg1', 'value'); 
$output = new Symfony\Component\Console\Output\ConsoleOutput(); 

呼叫命令的run方法:

$command = $this->get('MyCommandService'); 
$command->run($input, $ouput); 
+0

是的,這是好多了,thx! – PayteR

+0

$ output var用於捕獲控制檯回覆?謝謝 –

+0

是的,有一個'writeLine'方法在控制檯上寫一行 – Reuven

10

在我的環境(Symony 2.1)中,我必須對@Reuven解決方案進行一些修改才能使其工作。它們是:

服務定義 - 無變化。

在控制器:

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); 
} 
3

這裏有一個替代方案,可以讓你執行命令爲字符串你會在控制檯上以同樣的方式(也沒有必要爲這一個定義服務)。

你可以檢查this bundle's controller看看它是如何完成所有的細節。在這裏,我將總結它省略某些細節(例如處理環境,所以這裏所有命令都會在它們被調用的相同環境中運行)。

如果你只想從瀏覽器中運行的命令,你可以使用捆綁,因爲它是,但如果你想從任意控制器運行命令在這裏是如何做到這一點:

在你的控制器定義這樣的功能:

public function dumpassetsAction() 
{ 
    $output = $this->execute('assetic:dump'); 

    return new Response($output); 
} 

此外,您還需要定義一個類來作爲輸出緩衝器,b:

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; 
} 

然後你可以從這樣一個動作調用它ecause有沒有框架提供:

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; 
    } 
} 
+2

在LiipFunctionalTestBundle中還有一個相當不錯的實現,它使用php:// temp stream和內置的StreamOutput類來捕獲輸出,所以你不需要自己創建第二個輸出緩衝類。 https://github.com/liip/LiipFunctionalTestBundle/blob/master/Test/WebTestCase.php#L102 – Cvuorinen

+2

請注意,自從Symfony 2.4發佈以來,現在已經可以使用BufferedOutput了。 – noisebleed

1

如果您必須傳遞參數(和/或選項),那麼在v2.0.12中(對於更高版本可能爲true),您需要在實例化輸入對象之前指定InputDefinition。

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(); 



作爲一個側面說明,如果你使用的,如果你在你的命令中使用getContainer(),那麼下面的功能可以很方便您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(); 
} 
2

一樣@malloc 但

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); 
} 
2

你可以只是簡單的創建一個instanc你的命令的e和運行它:

/** 
* @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(); 
} 
0

如果運行所需要的env選項像assetic:dump

$stdout->writeln(sprintf('Dumping all <comment>%s</comment> assets.', $input->getOption('env'))); 

一個命令,你必須創建一個Symfony\Component\Console\Application並設置這樣的定義:

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); 

你可以將選項env設置爲該命令,因爲它不在其定義中。

+0

Silex Application擴展了Pimple Container並實現了HttpKernelInterface,TerminableInterface,並且沒有'setDefinition'方法。 – Trix