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

CLI, the other SAPI - PHP Amersfoort

Thijs Feryn
January 14, 2014

CLI, the other SAPI - PHP Amersfoort

Slides for my CLI talk at PHP Amersfoort

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