CLI,	
  the	
  other	
  SAPI
                                     PHPBarcelona	
  Conference
                                     Friday	
  October	
  28th	
  2011
                                     Barcelona,	
  Spain




                               php

                                       Thijs	
  Feryn
                                       Evangelist
                                       +32	
  (0)9	
  218	
  79	
  06
                                       thijs@combellgroup.com
About	
  me




 I’m	
  an	
  Evangelist	
  at	
  Combell
About	
  me




 I’m	
  a	
  board	
  member	
  at	
  PHPBenelux
Follow	
  me	
  on	
  Twi+er:	
  @ThijsFeryn

Give	
  me	
  feedback:	
  h+p://joind.in/4323
SAPI?




        The	
  way	
  you	
  interact	
  with	
  PHP
Common	
  SAPIs
Common	
  SAPIs


           •   Apache/Apache	
  2
           •   FPM
           •   FastCGI
           •   ISAPI
           •   CLI
           •   GTK
The	
  CLI	
  SAPI




PHP	
  script	
  execuYon	
  via	
  the	
  command	
  line	
  interface
When	
  to	
  use
When	
  to	
  use


•   In	
  crons
•   For	
  batch	
  tasks
•   For	
  worker	
  processes
•   Daemons
•   Process	
  control
•   InteracYon	
  with	
  other	
  binaries
CLI 101
CLI 101
The PHP binary
Passing arguments
Reading from STDIN
I/O with pipes
CLI 101
Invoking a script with the
        PHP binary

php	
  file.php
CLI 101
    Passing arguments



php	
  file.php	
  arg1	
  arg2
CLI 101
    interpreting arguments

<?php
echo "Number of arguments {$argc}n";
foreach($argv as $key=>$argument){
    echo "Argument # {$key}: {$argument}n"; 
}
CLI 101
    interpreting arguments
                    Argument	
  
       Argument	
     count
         array
<?php
echo "Number of arguments {$argc}n";
foreach($argv as $key=>$argument){
    echo "Argument # {$key}: {$argument}n"; 
}
CLI 101      The	
  
                 PHP	
  file	
  is	
  an	
  
interpreting arguments
                 argument	
  too
$	
  php	
  args.php	
  arg1	
  arg2
Number	
  of	
  arguments	
  3
Argument	
  #	
  0:	
  args.php
Argument	
  #	
  1:	
  arg1
Argument	
  #	
  2:	
  arg2
$
CLI 101
   interpreting arguments
$_SERVER[‘argc’]      $argc

$_SERVER[‘argv’]      $argv

   !!! register_argc_argv !!!
CLI 101
            getopt


<?php
$arguments = getopt('ab:c::');
var_dump($arguments);
CLI 101
            getopt           OpYonal	
  
              Flag	
  
           (no	
  value)      value
<?php
$arguments = getopt('ab:c::');
var_dump($arguments);
                           Required	
  
                            value
CLI 101
php	
  getopt.php	
  -­‐a	
  -­‐b	
  2	
  -­‐c3
array(3)	
  {
	
  	
  ["a"]=>                                  No	
  
	
  	
  bool(false)
	
  	
  ["b"]=>                              spacing	
  for	
  
	
  	
  string(1)	
  "2"                      opYonal	
  
	
  	
  ["c"]=>                              arguments
	
  	
  string(1)	
  "3"
}
CLI 101
      getopt: longopts


<?php
$arguments = getopt('',array('a
rg1','arg2:','arg3::'));
var_dump($arguments);
CLI 101
php	
  getopt2.php	
  -­‐-­‐arg1	
  -­‐-­‐arg2	
  123	
  -­‐-­‐arg3=x
array(3)	
  {
	
  	
  ["arg1"]=>
	
  	
  bool(false)                                 Mind	
  
	
  	
  ["arg2"]=>                           the	
  “=”	
  sign
	
  	
  string(3)	
  "123"
	
  	
  ["arg3"]=>
	
  	
  string(1)	
  "x"
}
CLI 101
     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);
CLI 101
$	
  cat	
  test.txt	
  |	
  php	
  stdin.php	
  
enO
owT
eerhT
$
CLI 101
$	
  cat	
  test.txt	
  |	
  php	
  stdin.php	
  
enO
owT
                 Output	
          Convert	
  
eerhT
$                 file           output	
  to	
  
                               input	
  with	
  
                                 pipes
Comparing	
  the	
  Apache	
  &	
  CLI	
  SAPI
Comparing	
  the	
  Apache	
  &	
  CLI	
  SAPI

                  Web	
  based	
  SAPI’s
•   HTTP	
  is	
  a	
  stateless	
  protocol
•   Request/response	
  based
•   Limited	
  interacYon
•   Sessions	
  &	
  cookies	
  as	
  workaround
•   ExecuYon	
  Ymeouts
•   Limited	
  request/response	
  size
Comparing	
  the	
  Apache	
  &	
  CLI	
  SAPI


                            CLI	
  SAPI
•   Controlable	
  state
•   Controlable	
  script	
  execuYon
•   ConYnuous	
  interacYon
•   No	
  need	
  for	
  sessions
•   No	
  execuYon	
  Ymeouts
The	
  PHP	
  binary




                       php
The	
  PHP	
  binary


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
InteracLve	
  mode	
  (-­‐a)

       $	
  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	
  >
InteracLve	
  mode	
  (-­‐a)


       $	
  php	
  -­‐a                                   Tab	
  
       Interactive	
  shell                            compleYon
       php	
  >	
  stri[TAB][TAB]
       strip_tags	
  	
  	
  	
  	
  stripcslashes	
  	
  
       stripslashes	
  	
  	
  stristr	
  	
  	
  	
  	
  	
  	
  	
  
       stripos	
  	
  	
  	
  	
  	
  	
  	
  
       php	
  >	
  stri
Run	
  code	
  (-­‐r)




      $	
  php	
  -­‐r	
  "echo	
  date('Y-­‐m-­‐d	
  H:i:s');"
      2011-­‐03-­‐02	
  22:04:45
      $
Config	
  directory	
  (-­‐c)




 $	
  php	
  -­‐c	
  /custom/dir/php.ini	
  script.php
Define	
  custom	
  INI	
  seRng	
  (-­‐d)



 $	
  php	
  -­‐d	
  max_execution_time=20	
  -­‐r	
  '$foo	
  =	
  
 ini_get("max_execution_time");	
  
 var_dump($foo);'
 string(2)	
  "20"
 $
Get	
  INI	
  informaLon	
  (-­‐i)
                                     Filtering	
  
                                      items
 $	
  php	
  -­‐i	
  |	
  grep	
  “log_”
 define_syslog_variables	
  =>	
  Off	
  =>	
  Off
 log_errors	
  =>	
  On	
  =>	
  On
 log_errors_max_len	
  =>	
  1024	
  =>	
  1024
 $
Syntax/lint	
  check	
  (-­‐l)
                                     Only	
  
                                 checks	
  parse	
  
                                    errors
 $	
  php	
  -­‐l	
  myFile.php
 No	
  syntax	
  errors	
  detected	
  in	
  myFile.php
 $
Module	
  list	
  (-­‐m)

 $	
  php	
  -­‐m
 [PHP	
  Modules]
 bcmath
 bz2
 calendar
 Core
 ctype
 curl
 date
 dba
 $
Syntax	
  highlighLng	
  (-­‐s)




<?php
echo	
  "Hello	
  world";


$	
  php	
  -­‐s	
  helloworld.php	
  >	
  helloworld.html
$
Syntax	
  highlighLng	
  (-­‐s)

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


<?php
echo "Hello world";
Version	
  info	
  (-­‐v)


 $	
  php	
  -­‐v
 PHP	
  5.3.3-­‐1ubuntu9.3	
  with	
  Suhosin-­‐Patch	
  
 (cli)	
  (built:	
  Jan	
  12	
  2011	
  16:07:38)	
  
 Copyright	
  (c)	
  1997-­‐2009	
  The	
  PHP	
  Group
 Zend	
  Engine	
  v2.3.0,	
  Copyright	
  (c)	
  1998-­‐2010	
  
 Zend	
  Technologies
 $
FuncLon	
  reflecLon	
  (-­‐-­‐rf)

$	
  php	
  -­‐-­‐rf	
  json_encode
Function	
  [	
  <internal:json>	
  function	
  
json_encode	
  ]	
  {

	
  	
  -­‐	
  Parameters	
  [2]	
  {
	
  	
  	
  	
  Parameter	
  #0	
  [	
  <required>	
  $value	
  ]
	
  	
  	
  	
  Parameter	
  #1	
  [	
  <optional>	
  $options	
  ]
	
  	
  }
}
$
Class	
  reflecLon	
  (-­‐-­‐rc)
 $	
  php	
  -­‐-­‐rc	
  stdclass
 Class	
  [	
  <internal:Core>	
  class	
  stdClass	
  ]	
  {
 	
  	
  -­‐	
  Constants	
  [0]	
  {
 	
  	
  }
 	
  	
  -­‐	
  Static	
  properties	
  [0]	
  {
 	
  	
  }
 	
  	
  -­‐	
  Static	
  methods	
  [0]	
  {
 	
  	
  }
 	
  	
  -­‐	
  Properties	
  [0]	
  {
 	
  	
  }
 	
  	
  -­‐	
  Methods	
  [0]	
  {
 	
  	
  }
 }
 $
Extension	
  reflecLon	
  (-­‐-­‐re)

$	
  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	
  ]
	
  	
  	
  	
  	
  	
  }
	
  	
  	
  	
  }
...
}
Extension	
  INI	
  informaLon	
  (-­‐-­‐ri)



 $	
  php	
  -­‐-­‐ri	
  pdo

 PDO

 PDO	
  support	
  =>	
  enabled
 PDO	
  drivers	
  =>	
  mysql,	
  sqlite,	
  sqlite2
 $
Back	
  on	
  track
Back	
  to	
  I/O
Input	
  &	
  output


             Web                   CLI
•   $_SERVER           •   $_SERVER
•   $_GET              •   $argc/$argv
•   $_POST             •   $_ENV
•   $_COOKIE           •   getopt()
•   $_SESSION          •   STDIN/STDOUT/
                           STDERR
•   $_ENV
Change	
  your	
  mindset
Change	
  your	
  mindset


        Don’t	
  use	
  sessions	
  &	
  cookies



             Just	
  use	
  local	
  variables
Change	
  your	
  mindset


       If	
  you	
  don’t	
  need	
  HTTP,	
  use	
  CLI

                                                    E.g.	
  
                                                  cronjobs
                  Avoid	
  overhead
Change	
  your	
  mindset


        Current	
  directory	
  !=	
  webroot

                                          CLI	
  
                                      scripts	
  are	
  
➡Use	
  dirname(__FILE__)            executable	
  
                                     everywhere
➡Use	
  chdir()
➡Use	
  getcwd()
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);
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);
STDIN


  <?php

  while(!feof(STDIN)){
      $line = trim(fgets(STDIN));
      if(strlen($line) > 0){
          echo strrev($line).PHP_EOL;
      }
  }
STDIN
                      Stream	
  
                   that	
  is	
  opened	
  
  <?php              by	
  default
  while(!feof(STDIN)){
      $line = trim(fgets(STDIN));
      if(strlen($line) > 0){
          echo strrev($line).PHP_EOL;
      }
  }
STDIN


            The	
                        Stream	
  
                                     that	
  is	
  opened	
  
           proof	
  !                  by	
  default
 $	
  php	
  -­‐r	
  "var_dump(STDIN);"
 resource(1)	
  of	
  type	
  (stream)
 $
Wordcount	
  example

<?php
$wordArray = array();
while(!feof(STDIN)){
    $line = trim(fgets(STDIN));
    if(strlen($line) > 0){
        foreach(preg_split('/[s]+/',$line) as $word){
            if(!array_key_exists($word,$wordArray)){
                $wordArray[$word] = 0;
            }
            $wordArray[$word]++;
        }
    }
}
ksort($wordArray);
foreach($wordArray as $word=>$count){
    echo "$word: $count".PHP_EOL;
}
Wordcount	
  example

$	
  cat	
  wordcount.txt	
  
Barcelona	
  Thijs
Thijs
Barcelona
Thijs	
  PHP
Thijs
Barcelona
$	
  cat	
  wordcount.txt	
  	
  |	
  php	
  wordcount.php	
  
PHP:	
  1
Thijs:	
  4
Barcelona:	
  3
$
STDOUT
                            STDOUT	
  
                              ==	
  
                             echo
<?php
$handle = fopen('php://stdout','w');
fwrite($handle,'Hello world');
fclose($handle);
STDOUT




<?php
fwrite(STDOUT,'Hello world');
STDERR




<?php
$handle = fopen('php://stderr','w');
fwrite($handle,'Serious error!');
fclose($handle);
STDERR




<?php
fwrite(STDERR,'Serious error!');
Mixing	
  STDOUT	
  &	
  STDERR


<?php
fwrite(STDOUT,'STDOUT output'.PHP_EOL);
fwrite(STDERR,'STDERR output'.PHP_EOL);

$	
  php	
  stdmix.php	
  
STDOUT	
  output
STDERR	
  output
$
Mixing	
  STDOUT	
  &	
  STDERR


<?php
                Looks	
  
fwrite(STDOUT,'STDOUT output'.PHP_EOL);
              the	
  same
fwrite(STDERR,'STDERR output'.PHP_EOL);

$	
  php	
  stdmix.php	
  
STDOUT	
  output
STDERR	
  output
$
Mixing	
  STDOUT	
  &	
  STDERR


$	
  php	
  stdmix.php	
  >	
  /dev/null	
  
STDERR	
  output
$


$	
  php	
  stdmix.php	
  &>	
  	
  /dev/null
$
Mixing	
  STDOUT	
  &	
  STDERR
                                                 STDOUT	
  
                                                is	
  caught
$	
  php	
  stdmix.php	
  >	
  /dev/null	
           STDOUT	
  
STDERR	
  output
$                                               &	
  STDERR	
  are	
  
                                                      caught
$	
  php	
  stdmix.php	
  &>	
  	
  /dev/null
$
AlternaLve	
  output



<?php
fclose(STDOUT);
$handle = fopen(realpath(dirname(__FILE__).'/
output.txt'),'a');
echo "Hello world!".PHP_EOL;
fclose($handle);
AlternaLve	
  output



<?php
fclose(STDOUT);
$handle = fopen(realpath(dirname(__FILE__).'/
output.txt'),'a');
echo "Hello world!".PHP_EOL;
fclose($handle);
                             echo	
  
                       output	
  is	
  wrioen	
  
                            to	
  file
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
$
Readline


<?php
$name = readline("What's your name: ");
$location = readline("Where do you live: ");
echo PHP_EOL."Hello $name from $locationn";

$	
  php	
  readline.php	
  
What's	
  your	
  name:	
  Thijs
Where	
  do	
  you	
  live:	
  Belgium

Hello	
  Thijs	
  from	
  Belgium
$
Shebang	
  !
Shebang	
  !


#!/usr/bin/php
<?php
echo "Hello world".PHP_EOL;


$	
  chmod	
  +x	
  shebang.php
$	
  ./shebang.php
Hello	
  world
$
Encore
Process	
  Control	
  should	
  not	
  be	
  enabled	
  
  within	
  a	
  web	
  server	
  environment	
  and	
  
  unexpected	
  results	
  may	
  happen	
  if	
  any	
  
Process	
  Control	
  funcYons	
  are	
  used	
  within	
  
       a	
  web	
  server	
  environment.
Forking       PID	
  value	
  
             determines	
  
               context                        Copy	
  
<?php
                                            program	
  
$pid = pcntl_fork();
if ($pid == -1) {
                                            execuYon
  //Forking failed
} else if ($pid) {
  //Parent logic
} else {               PID	
  of	
  child	
  
  //Child logic
}                        process
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;
}
Forking                     Perform	
  
                            forking
<?php
$pid = pcntl_fork();
if ($pid == -1) {
     die('could not fork');
                                Wait	
  for	
  child	
  
} else if ($pid) {              terminaYon
     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;
}
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);
Signals

<?php
declare(ticks = 1);           Process	
  
                            terminaYon
function sig_handler($signo)
{
     switch ($signo) {
         case SIGTERM:
                                           Process	
  
             echo PHP_EOL."SIGTERM".PHP_EOL;
             exit();
             break;
         case SIGINT:                   interrupYon
             echo PHP_EOL."SIGINT".PHP_EOL;
             exit();
             break;

                                           Catch	
  signals
     }
}
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
sleep(100);
POSIX	
  process	
  control	
  funcLons
<?php
echo "[prefork] PID:
".posix_getpid().", parent PID: ".posix_getppid().PH
P_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);
Parent	
  PID	
                   Prefork	
  PID	
  
POSIX	
  process	
  c==	
   funcLons
    of	
  parent	
   ontrol	
              ==	
  
<?phpsession	
  PID                    parent	
  PID
echo "[prefork] PID:
".posix_getpid().", parent PID: ".posix_getppid().PH
P_EOL;
$pid = pcntl_fork();
if ($pid == -1) {                            child	
  PID
     die('could not fork');
   child	
  PID
} else if ($pid == 0) {
        echo "[child] PID: ".posix_getpid().", paren
t PID: ".posix_getppid().PHP_EOL;               parent	
  
        exit;
} else {                                         PID
        echo "[parent] PID: ".posix_getpid().", pare
nt PID: ".posix_getppid().PHP_EOL;
        pcntl_wait($status);
}
POSIX	
  process	
  control	
  funcLons

<?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;
}
POSIX	
  process	
  control	
  funcLons
                           “KILL	
  0”	
  checks	
  
<?php
$pid=pcntl_fork();            existence
if ($pid == -1) {
    die("could not fork");
} else if ($pid) {
                                       Send	
  SIGTERM	
  
    $exists = posix_kill($pid,0)?'still':'no longer';
    echo "[parent] Child process $pid $exists exists".PHP_EOL;
                                           signal
    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){
                                             Child	
  process	
  
        sleep(100);
    }
                                                is	
  dead
    exit;
}
Talk	
  
                                                      dedicated	
  to	
  
                                                     process	
  control	
  
Check	
  this	
  
guy	
  out	
  !                                          in	
  PHP
                      Jeroen	
  Keppens:	
  @jkeppens
                    http://www.slideshare.net/jkeppens/
                       zendcon-­‐2011-­‐php-­‐in-­‐the-­‐dark
•January	
  27th	
  &	
  28th	
  2012
•Best	
  Western	
  Hotel	
  Ter	
  Elst	
  Antwerp	
  (Belgium)
•Call	
  For	
  Papers	
  CLOSED
•Schedule	
  &	
  Ycket	
  announcements	
  in	
  a	
  couple	
  
of	
  weeks
Q&A
Thanks	
  !

Cli the other sapi pbc11