Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CLI, the other SAPI - PHP Amersfoort

Avatar for Thijs Feryn Thijs Feryn
January 14, 2014

CLI, the other SAPI - PHP Amersfoort

Slides for my CLI talk at PHP Amersfoort

Avatar for Thijs Feryn

Thijs Feryn

January 14, 2014
Tweet

More Decks by Thijs Feryn

Other Decks in Technology

Transcript

  1. • In crons • For batch tasks • For worker

    processes • Daemons • Process control • Interaction with other binaries
  2. Interpreting arguments <?php! echo "Number of arguments {$argc}\n";! foreach($argv as

    $key=>$argument){! echo "Argument # {$key}: {$argument} \n"; ! }
  3. Interpreting arguments <?php! echo "Number of arguments {$argc}\n";! foreach($argv as

    $key=>$argument){! echo "Argument # {$key}: {$argument} \n"; ! } Argument   count Argument   array
  4. Interpreting arguments $  php  args.php  arg1  arg2   Number  of

     arguments  3   Argument  #  0:  args.php   Argument  #  1:  arg1   Argument  #  2:  arg2   $   The   PHP  file  is  an   argument  too
  5. GETOPT php  getopt.php  -­‐a  -­‐b  2  -­‐c3   array(3)  {

         ["a"]=>      bool(false)      ["b"]=>      string(1)  "2"      ["c"]=>      string(1)  "3"   }   No   spacing  for   op8onal   arguments
  6. GETOPT Long options php  getopt2.php  -­‐-­‐arg1  -­‐-­‐arg2  123  -­‐-­‐arg3=x  

    array(3)  {      ["arg1"]=>      bool(false)      ["arg2"]=>      string(3)  "123"      ["arg3"]=>      string(1)  "x"   }   Mind   the  “=”  
  7. Reading from STDIN <?php! $handle = fopen('php://stdin','r');! while(!feof($handle)){! $line =

    trim(fgets($handle));! if(strlen($line) > 0){! echo strrev($line).PHP_EOL;! }! }! fclose($handle);
  8. Reading from STDIN $  cat  test.txt  |  php  stdin.php  

      enO   owT   eerhT   $ Output   file Convert   output  to   input  with   pipes
  9. Web vs CLI Web! CLI HTTP is a stateless protocol

    Controlable state Request/response based Controllable script execution Limited interaction Continuous interaction Execution timeouts No sessions required GET/POST/COOKIE/SESSION Arguments, STDIN, STDOUT, STDERR, exit codes
  10. Usage:  php  [options]  [-­‐f]  <file>  [-­‐-­‐]  [args...]      

             php  [options]  -­‐r  <code>  [-­‐-­‐]  [args...]                php  [options]  [-­‐B  <begin_code>]  -­‐R  <code>   [-­‐E  <end_code>]  [-­‐-­‐]  [args...]                php  [options]  [-­‐B  <begin_code>]  -­‐F  <file>   [-­‐E  <end_code>]  [-­‐-­‐]  [args...]                php  [options]  -­‐-­‐  [args...]                php  [options]  -­‐a The PHP binary
  11. Interactive mode $  php  -­‐a   Interactive  shell   !

    php  >  echo  5+8;   13   php  >  function  addTwo($n)   php  >  {   php  {  return  $n  +  2;   php  {  }   php  >  var_dump(addtwo(2));   int(4)   php  >  
  12. Interactive mode $  php  -­‐a   Interactive  shell   !

    php  >  stri[TAB][TAB]   strip_tags          stripcslashes     stripslashes      stristr                 stripos                   php  >  stri   ! Tab   comple8on
  13. Custom ini settings $  php  -­‐d  max_execution_time=20  -­‐r  '$foo  =

      ini_get("max_execution_time");   var_dump($foo);'   string(2)  "20"   $
  14. Get ini settings $  php  -­‐i  |  grep  “log_”  

    define_syslog_variables  =>  Off  =>  Off   log_errors  =>  On  =>  On   log_errors_max_len  =>  1024  =>  1024   $ Filtering   items
  15. Lint checking $  php  -­‐l  myFile.php   No  syntax  errors

     detected  in  myFile.php   $ Only   checks  parse   errors
  16. List modules $  php  -­‐m   [PHP  Modules]   bcmath

      bz2   calendar   Core   ctype   curl   date   dba   $
  17. Syntax highlighting <?php! echo "Hello world"; <code><span style="color: #000000">! <span

    style="color: #0000BB">&lt;?php<br / ></span><span style="color: #007700">echo&nbsp;</span><span style="color: #DD0000">"Hello&nbsp;world"</span><span style="color: #007700">;</span>! </span>!
  18. Version $  php  -­‐v   PHP  5.4.17  (cli)  (built:  Aug

     25  2013   02:03:38)   Copyright  (c)  1997-­‐2013  The  PHP  Group   Zend  Engine  v2.4.0,  Copyright  (c)  1998-­‐2013   Zend  Technologies   $
  19. Function reflection $  php  -­‐-­‐rf  json_encode   Function  [  <internal:json>

     function   json_encode  ]  {   !    -­‐  Parameters  [2]  {          Parameter  #0  [  <required>  $value  ]          Parameter  #1  [  <optional>  $options  ]      }   }   $
  20. Class reflection $  php  -­‐-­‐rc  stdclass   Class  [  <internal:Core>

     class  stdClass  ]  {      -­‐  Constants  [0]  {      }      -­‐  Static  properties  [0]  {      }      -­‐  Static  methods  [0]  {      }      -­‐  Properties  [0]  {      }      -­‐  Methods  [0]  {      }   }   $
  21. Extension reflection $  php  -­‐-­‐re  json   Extension  [  <persistent>

     extension  #20  json  version   1.2.1  ]  {   ...      -­‐  Functions  {          Function  [  <internal:json>  function  json_encode  ]  {   !            -­‐  Parameters  [2]  {                  Parameter  #0  [  <required>  $value  ]                  Parameter  #1  [  <optional>  $options  ]              }          }   ...   }  
  22. Extension information $  php  -­‐-­‐ri  pdo   ! PDO  

    ! PDO  support  =>  enabled   PDO  drivers  =>  mysql,  sqlite,  sqlite2   $
  23. Built-in webserver $  php  -­‐S  0:8080  -­‐t  /var/www  /var/www/index.php  

    $   PHP  5.4.17  Development  Server  started  at  Mon   Jan  13  15:35:03  2014   Listening  on  http://0:8080   Document  root  is  /var/www   Press  Ctrl-­‐C  to  quit.
  24. Built-in webserver $  php  -­‐S  0:8080  -­‐t  /var/www  /var/www/index.php  

    $   PHP  5.4.17  Development  Server  started  at  Mon   Jan  13  15:35:03  2014   Listening  on  http://0:8080   Document  root  is  /var/www   Press  Ctrl-­‐C  to  quit. Docroot Router IP   &  port
  25. <?php! if (php_sapi_name() == 'cli- server' && preg_match('/\.(?:png|jpg|jpeg| gif|css|js|ico|ttf|woff) $/',

    $_SERVER["REQUEST_URI"])) {! return false;! } Built-in webserver Handling   sta8c  files
  26. I/O

  27. Current directory !== webroot • Use dirname(__FILE__) • Use chdir()

    • Use getcwd() CLI   scripts  are   executable   everywhere
  28. STDIN <?php! ! while(!feof(STDIN)){! $line = trim(fgets(STDIN));! if(strlen($line) > 0){!

    echo strrev($line).PHP_EOL;! }! }! Stream   that  is  opened   by  default
  29. Mixing STDOUT & STDERR $  php  stdmix.php  >  /dev/null  

      STDERR  output   $ $  php  stdmix.php  &>    /dev/null   $
  30. Alternative output <?php! fclose(STDOUT);! $handle = fopen(realpath(dirname(__FILE__).'/ output.txt'),'a');! echo "Hello

    world!".PHP_EOL;! fclose($handle); echo   output  is  wriQen   to  file
  31. Piping $  php  -­‐r  'for($i=0;$i<10;$i++)  echo  $i.PHP_EOL;'   0  

    1   2   3   4   5   6   7   8   9   $  php  -­‐r  'for($i=0;$i<10;$i++)  echo  $i.PHP_EOL;'  |  wc  -­‐l              10   $
  32. Readline <?php! $name = readline("What's your name: ");! $location =

    readline("Where do you live: ");! echo PHP_EOL."Hello $name from $location\n"; $  php  readline.php     What's  your  name:  Thijs   Where  do  you  live:  Belgium   ! Hello  Thijs  from  Belgium   $
  33. Symfony console component <?php! date_default_timezone_set('Europe/Brussels');! set_time_limit(0);! require __DIR__ . ‚/../vendor/autoload.php';!

    ! use Symfony\Component\Console\Application;! use Symfony\Component\Console\Command\Command;! use Symfony\Component\Console\Input\InputInterface;! use Symfony\Component\Console\Output\OutputInterface;! use Symfony\Component\Console\Input\InputArgument;! use Symfony\Component\Console\Input\InputOption;! ! $app = new Application('My CLI Application', '0.1.0');! $app->addCommands(array(! new Thijs\Console\Command\TestCommand()! ));! $app->run();
  34. namespace Thijs\Console\Command;! ! use Symfony\Component\Console\Command\Command;! use Symfony\Component\Console\Input\InputInterface;! use Symfony\Component\Console\Output\OutputInterface;! use

    Symfony\Component\Console\Input\InputArgument;! use Symfony\Component\Console\Input\InputOption;! use Symfony\Component\Console\Formatter\OutputFormatterStyle;! ! class TestCommand extends Command {! } The command class
  35. protected function configure() {! $this->setName("hello")! ->setDescription("Saying hello to the world")!

    ->addArgument('name',InputArgument::REQUIRED,'Your name')! ->addOption(! 'yell',! null,! InputOption::VALUE_NONE,! 'If set, the task will yell in uppercase letters'! )! ->setHelp('The <info>'.$this- >getName().'<info> command says hello to the world');! } Configure the command
  36. protected function execute(InputInterface $input, OutputInterface $output) {! $text = 'Hello

    '.$input->getArgument('name');! if($input->getOption('yell')){! $text = strtoupper($text);! }! $output->writeln($text);! } Execute the command
  37. $dialog = $this->getHelperSet()->get('dialog');! while($dialog->askConfirmation(! $output,! ’<question>Continue with this action? [y/n]:

    </question>’,! false)){! $text = 'Hello ‚.$dialog->ask($output,! ! ! ! ! ! ! 'Who do you want to say hello to ?: ', 'world');! if($input->getOption('yell')){! $text = strtoupper($text);! }! $this->_hello[] = array($text);! } Adding dialogs
  38. $progress = $this->getHelperSet()->get('progress');! $progress->start($output, 50);! $i = 0;! while ($i++

    < 50) {! ! $progress->advance();! usleep(50000);! }! $progress->finish(); Adding progress
  39. Adding table output bin/console.php  hello  Thijs   Continue  with  this

     action?  [y/n]:  y   Who  do  you  want  to  say  hello  to  ?:  PHP   Continue  with  this  action?  [y/n]:  y   Who  do  you  want  to  say  hello  to  ?:  ElePHPants   Continue  with  this  action?  [y/n]:  n   Processing  input   Please  wait    50/50  [============================]  100%   +-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+   |  Hello                        |   +-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+   |  Hello  Thijs            |   |  Hello  PHP                |   |  Hello  ElePHPants  |   +-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+
  40. Process Control should not be enabled within a web server

    environment and unexpected results may happen if any Process Control functions are used within a web server environment.
  41. Forking <?php! $pid = pcntl_fork();! if ($pid == -1) {!

    //Forking failed! } else if ($pid) {! //Parent logic! } else {! //Child logic! }
  42. Forking <?php! $pid = pcntl_fork();! if ($pid == -1) {!

    //Forking failed! } else if ($pid) {! //Parent logic! } else {! //Child logic! } Copy   program   execu8on PID   value  determines   context PID  of  child   process
  43. Forking <?php! $pid = pcntl_fork();! if ($pid == -1) {!

    die('could not fork');! } else if ($pid) {! echo "[parent] Starting".PHP_EOL;! pcntl_wait($status);! echo "[parent] Exiting".PHP_EOL;! } else {! echo "[child] Starting".PHP_EOL;! for($i=0;$i<3;$i++){! echo "[child] Loop $i".PHP_EOL;! sleep(1);! }! echo "[child] Exiting".PHP_EOL;! exit;! }
  44. Forking <?php! $pid = pcntl_fork();! if ($pid == -1) {!

    die('could not fork');! } else if ($pid) {! echo "[parent] Starting".PHP_EOL;! pcntl_wait($status);! echo "[parent] Exiting".PHP_EOL;! } else {! echo "[child] Starting".PHP_EOL;! for($i=0;$i<3;$i++){! echo "[child] Loop $i".PHP_EOL;! sleep(1);! }! echo "[child] Exiting".PHP_EOL;! exit;! } Perform   forking Wait   for  child   termina8on
  45. Signals <?php! declare(ticks = 1);! function sig_handler($signo)! {! switch ($signo)

    {! case SIGTERM:! echo PHP_EOL."SIGTERM".PHP_EOL;! exit();! break;! case SIGINT:! echo PHP_EOL."SIGINT".PHP_EOL;! exit();! break;! }! }! pcntl_signal(SIGTERM, "sig_handler");! pcntl_signal(SIGINT, "sig_handler");! sleep(100);
  46. Signals <?php! declare(ticks = 1);! function sig_handler($signo)! {! switch ($signo)

    {! case SIGTERM:! echo PHP_EOL."SIGTERM".PHP_EOL;! exit();! break;! case SIGINT:! echo PHP_EOL."SIGINT".PHP_EOL;! exit();! break;! }! }! pcntl_signal(SIGTERM, "sig_handler");! pcntl_signal(SIGINT, "sig_handler");! sleep(100); Process   termina8on Process   interrup8on Catch   signals
  47. POSIX <?php! echo "[prefork] PID: ".posix_getpid().", parent PID: ".posix_getppid() .PHP_EOL;!

    $pid = pcntl_fork();! if ($pid == -1) {! die('could not fork');! } else if ($pid == 0) {! echo "[child] PID: ".posix_getpid().", parent PID : ".posix_getppid().PHP_EOL;! exit;! } else {! echo "[parent] PID: ".posix_getpid().", parent PID: ".posix_getppid().PHP_EOL;! pcntl_wait($status);! }
  48. POSIX <?php! $pid=pcntl_fork();! if ($pid == -1) {! die("could not

    fork");! } else if ($pid) {! $exists = posix_kill($pid,0)?'still':'no longer';! echo "[parent] Child process $pid $exists exists".PHP_EOL ;! echo "[parent] Killing child process $pid".PHP_EOL;! posix_kill($pid,SIGTERM);! echo "[parent] Child process $pid killed".PHP_EOL;! pcntl_wait($status);! $exists = posix_kill($pid,0)?'still':'no longer';! echo "[parent] Child process $pid $exists exists".PHP_EOL ;! } else {! while(true){! sleep(100);! }! exit;! }