2014-05-05 16 views
9

Tôi có lệnh Symfony Console lặp lại trên bộ sưu tập các mục có khả năng lớn và thực hiện nhiệm vụ với từng mục. Vì bộ sưu tập có thể lớn, lệnh có thể mất nhiều thời gian để chạy (giờ). Khi lệnh kết thúc, nó sẽ hiển thị một số thống kê.Hủy và tiếp tục lệnh Symfony Console

Tôi muốn làm cho nó có thể hủy bỏ lệnh một cách tốt đẹp. Ngay bây giờ nếu tôi hủy bỏ nó (tức là với ctrl + c trong CLI), không có tóm tắt thống kê và không có cách nào để xuất các tham số cần thiết để tiếp tục lệnh. Một vấn đề khác là lệnh có thể bị chấm dứt ở giữa việc xử lý một mục - nó sẽ tốt hơn nếu nó chỉ có thể chấm dứt giữa các mục xử lý.

Vì vậy, có cách nào để yêu cầu lệnh "hủy bỏ độc đáo càng sớm càng tốt" hoặc có lệnh ctrl + c được hiểu là như vậy không?

Tôi đã thử sử dụng sự kiện ConsoleEvents::TERMINATE, mặc dù các trình xử lý cho điều này chỉ được kích hoạt khi hoàn thành lệnh, chứ không phải khi tôi ctrl + c điều. Và tôi đã không thể tìm thêm thông tin về việc thực hiện các lệnh có thể tiếp tục như vậy.

+0

Tôi nghĩ bằng cách tạo đầu vào tương tác, bạn sẽ có thể khắc phục sự cố, nhưng tôi không biết chính xác bạn nên triển khai như thế nào để * trên nhấn phím cụ thể chấm dứt lệnh và cung cấp cho bạn số liệu thống kê * Liên kết này có thể giúp [http://davidbu.ch/slides/20130613_techtalk_symfony-console.html] từng bước tạo lệnh tương tác – Javad

+0

Hoặc bạn có thể kiểm tra trình xử lý sự kiện trong lệnh và dựa vào đó chấm dứt lệnh của bạn [http: // symfony .com/doc/current/components/console/events.html] – Javad

Trả lời

17

Đây là những gì làm việc cho tôi. Bạn cần gọi pcntl_signal_dispatch trước khi các trình xử lý tín hiệu thực sự được thực hiện. Nếu không có nó, tất cả các nhiệm vụ sẽ hoàn thành đầu tiên.

<?php 
use Symfony\Component\Console\Command\Command; 

class YourCommand extends Command 
{ 
    protected function execute(InputInterface $input, OutputInterface $output) 
    { 
     pcntl_signal(SIGTERM, [$this, 'stopCommand']); 
     pcntl_signal(SIGINT, [$this, 'stopCommand']); 

     $this->shouldStop = false; 

     foreach ($this->tasks as $task) 
     { 
      pcntl_signal_dispatch(); 
      if ($this->shouldStop) break; 
      $task->execute(); 
     } 

     $this->showSomeStats($output); 
    } 

    public function stopCommand() 
    { 
     $this->shouldStop = true; 
    } 
} 
+1

Đó là những gì tôi đang tìm kiếm, cảm ơn, nhưng một gợi ý: Bạn phải thiết lập khai báo (ticks = 1); http://php.net/manual/en/function.pcntl-signal.php#refsect1-function.pcntl-signal-changelog – createproblem

+0

Cảm ơn, vì PHP 7.1 bạn cũng có thể bật xử lý tín hiệu không đồng bộ bằng cách thực thi 'pcntl_async_signals (true) ; 'http://php.net/manual/en/function.pcntl-async-signals.php – Karim

2

Bạn nên xem xét xử lý tín hiệu của RabbitMqBundle. Phương thức execute của nó chỉ liên kết một số cuộc gọi lại thông qua cuộc gọi hàm pcntl_signal(). Một trường hợp phổ biến sẽ trông khá giống như thế này:

<?php 
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand as Command; 

class YourCommand extends Command 
{ 
    protected function execute(InputInterface $input, OutputInterface $output) 
    { 
     pcntl_signal(SIGTERM, array(&$this, 'stopCommand', $output)); 
     pcntl_signal(SIGINT, array(&$this, 'stopCommand', $output)); 
     pcntl_signal(SIGHUP, array(&$this, 'restartCommand', $output)); 

     // The real execute method body 
    } 

    public function stopCommand(OutputInterface $output) 
    { 
     $output->writeln('Stopping'); 

     // Do what you need to stop your process 
    } 

    public function restartCommand(OutputInterface $output) 
    { 
     $output->writeln('Restarting'); 

     // Do what you need to restart your process 
    } 
} 
+0

Cảm ơn rất nhiều, điều này và các nhận xét ở trên đã khiến tôi tìm ra giải pháp. Ví dụ bạn đang thiếu một cuộc gọi đến pcntl_signal_dispatch và cũng có một lỗi cú pháp: một mảng gọi lại chỉ có hai phần tử. Bạn không thể nhồi nhét trong tranh cãi sau những điều đó. –

+0

@ JeroenDeDauw, oops, đây là một số mã giả, xấu của tôi :) – kix

2

Câu trả lời phức tạp hơn mức cần thiết. Chắc chắn, bạn có thể đăng ký bộ xử lý tín hiệu POSIX, nhưng nếu các tín hiệu duy nhất cần được xử lý là ngắt cơ bản và tương tự, bạn chỉ cần xác định một destructor trên Command.


class YourCommand extends Command 
{ 
    // Other code goes here. 

    __destruct() 
    { 
     $this->shouldStop = true; 
    } 
} 

Một trường hợp bạn muốn đăng ký một tín hiệu POSIX là cho SIGCONT tín hiệu, mà có thể xử lý việc nối lại của một quá trình mà đã được ngừng lại (SIGSTOP).

Một trường hợp khác sẽ là nơi bạn muốn mọi tín hiệu hoạt động khác nhau; đối với hầu hết các phần, mặc dù, SIGINTSIGTERM và một số ít người khác sẽ được đăng ký với cùng một quy trình "OMG THE PROCESS HAS BEEN KILLED".

Ngoài những ví dụ này, việc đăng ký các sự kiện tín hiệu là không cần thiết. Đây là lý do tại sao destructors tồn tại.

Bạn thậm chí có thể mở rộng lớp cơ sở của Symfony Command với phương thức __destruct, sẽ tự động cung cấp dọn dẹp cho mọi lệnh; nên một lệnh cụ thể yêu cầu hoạt động bổ sung, chỉ cần ghi đè lên nó.

Các vấn đề liên quan