98
CLI, the other SAPI Thijs Feryn Evangelist +32 (0)9 218 79 06 [email protected] Confoo Thursday March 10th 2011 Montreal, Canada php

Cli the other SAPI confoo11

Embed Size (px)

DESCRIPTION

My command line PHP talks for Confoo 2011 in Montreal Canada.

Citation preview

Page 1: Cli the other SAPI confoo11

CLI,  the  other  SAPI

Thijs  FerynEvangelist+32  (0)9  218  79  [email protected]

ConfooThursday  March  10th  2011Montreal,  Canada

php

Page 2: Cli the other SAPI confoo11

About  me

I’m  an  Evangelist  at  Combell

Page 3: Cli the other SAPI confoo11

About  me

I’m  a  board  member  at  PHPBenelux

Page 4: Cli the other SAPI confoo11

I  live  in  the  wonderful  city  of  Bruges

MPBecker  -­‐  Bruges  by  Night  hWp://www.flickr.com/photos/galverson2/3715965933

Page 5: Cli the other SAPI confoo11

Follow  me  on  Twi+er:  @ThijsFeryn

Read  my  blog:  h+p://blog.feryn.eu

Give  me  feedback:  h+p://joind.in/2852

Page 6: Cli the other SAPI confoo11
Page 7: Cli the other SAPI confoo11

SAPI  according  to  Wikipedia

“The  Server  Applica^on  Programming  Interface  (SAPI)  is  the  generic  term  used  to  designate  direct  module  interfaces  to  web  server  applica^ons”

Page 8: Cli the other SAPI confoo11

SAPI  according  to  Wikipedia

The  way  you  interact  with  PHP

Page 9: Cli the other SAPI confoo11

Common  SAPIs

• Apache/Apache  2

• CGI

• FastCGI

• ISAPI

• CLI

• GTK

Page 10: Cli the other SAPI confoo11

The  CLI  SAPI  according  to  php.net

As  of  version  4.3.0,  PHP  supports  a  new  SAPI  type  (Server  Applica^on  Programming  Interface)  named  CLI  which  means  Command  Line  Interface.  As  the  name  implies,  this  SAPI  type  main  focus  is  on  developing  shell  (or  desktop  as  well)  applica^ons  with  PHP

Page 11: Cli the other SAPI confoo11

The  CLI  SAPI  according  to  php.net

PHP  script  execu^on  via  the  command  line  interface

Page 12: Cli the other SAPI confoo11

When  to  use

• In  crons

• For  batch  tasks

• For  worker  processes

• Daemons

• Process  control

• Interac^on  with  other  binaries

Page 13: Cli the other SAPI confoo11

CLI 101

Page 14: Cli the other SAPI confoo11

CLI 101

The PHP binary

Passing arguments

Reading from STDIN

I/O with pipes

Page 15: Cli the other SAPI confoo11

CLI 101Invoking a script with the

PHP binary

php  file.php

Page 16: Cli the other SAPI confoo11

CLI 101Passing arguments

php  file.php  arg1  arg2

Page 17: Cli the other SAPI confoo11

CLI 101interpreting arguments

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

Page 18: Cli the other SAPI confoo11

CLI 101interpreting arguments

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

Argument  countArgument  

array

Page 19: Cli the other SAPI confoo11

CLI 101interpreting arguments

$  php  args.php  arg1  arg2Number  of  arguments  3Argument  #  0:  args.phpArgument  #  1:  arg1Argument  #  2:  arg2$

The  PHP  file  is  an  argument  too

Page 20: Cli the other SAPI confoo11

CLI 101interpreting arguments

$argc

$argv

$_SERVER[‘argc’]

$_SERVER[‘argv’]

!!! register_argc_argv !!!

Page 21: Cli the other SAPI confoo11

CLI 101getopt

<?php$arguments = getopt('ab:c::');var_dump($arguments);

Page 22: Cli the other SAPI confoo11

CLI 101getopt

<?php$arguments = getopt('ab:c::');var_dump($arguments);

Op^onal  value

Flag  (no  value)

Required  value

Page 23: Cli the other SAPI confoo11

CLI 101php  getopt.php  -­‐a  -­‐b  2  -­‐c3array(3)  {    ["a"]=>    bool(false)    ["b"]=>    string(1)  "2"    ["c"]=>    string(1)  "3"}

No  spacing  for  op^onal  arguments

Page 24: Cli the other SAPI confoo11

CLI 101getopt: longopts

<?php$arguments = getopt('',array('arg1','arg2:','arg3::'));var_dump($arguments);

Page 25: Cli the other SAPI confoo11

CLI 101php  getopt2.php  -­‐-­‐arg1  -­‐-­‐arg2  123  -­‐-­‐arg3=xarray(3)  {    ["arg1"]=>    bool(false)    ["arg2"]=>    string(3)  "123"    ["arg3"]=>    string(1)  "x"}

Mind  the  “=”  sign

Page 26: Cli the other SAPI confoo11

CLI 101REading 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);

Page 27: Cli the other SAPI confoo11

CLI 101$  cat  test.txt  |  php  stdin.php  enOowTeerhT$

Page 28: Cli the other SAPI confoo11

CLI 101$  cat  test.txt  |  php  stdin.php  enOowTeerhT$

Output  file

Convert  output  to  input  with  pipes

Page 29: Cli the other SAPI confoo11

Comparing  the  Apache  &  CLI  SAPI

Page 30: Cli the other SAPI confoo11

Comparing  the  Apache  &  CLI  SAPI

Web  based  SAPI’s• HTTP  is  a  stateless  protocol

• Request/response  based

• Limited  interac^on

• Sessions  &  cookies  as  workaround

• Execu^on  ^meouts

• Limited  request/response  size

Page 31: Cli the other SAPI confoo11

Comparing  the  Apache  &  CLI  SAPI

CLI  SAPI• Controlable  state

• Controlable  script  execu^on

• Con^nuous  interac^on

• No  need  for  sessions

• No  execu^on  ^meouts

Page 32: Cli the other SAPI confoo11

Measuring  the  state

Page 33: Cli the other SAPI confoo11

Input  &  output

Page 34: Cli the other SAPI confoo11

Input  &  output

Web• $_SERVER

• $_GET

• $_POST

• $_COOKIE

• $_SESSION

• $_ENV

CLI• $_SERVER

• $argc/$argv

• $_ENV

• getopt()

• STDIN/STDOUT/STDERR

Page 35: Cli the other SAPI confoo11

Change  your  mindset

Page 36: Cli the other SAPI confoo11

Change  your  mindset

Don’t  use  sessions  &  cookies

Just  use  local  variables

Page 37: Cli the other SAPI confoo11

Change  your  mindset

Don’t  “get”  your  input

Just  “read”  it

Page 38: Cli the other SAPI confoo11

Change  your  mindset

Don’t  bundle  your  output

You  can  use  distributed  output

Page 39: Cli the other SAPI confoo11

Change  your  mindset

If  you  don’t  need  HTTP,  use  CLI

Avoid  overhead

E.g.  cronjobs

Page 40: Cli the other SAPI confoo11

Change  your  mindset

Current  directory  !=  webroot

➡Use  dirname(__FILE__)

➡Use  chdir()

➡Use  getcwd()

CLI  scripts  are  executable  everywhere

Page 41: Cli the other SAPI confoo11

The  PHP  binary

php

Page 42: Cli the other SAPI confoo11

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

Page 43: Cli the other SAPI confoo11

InteracOve  mode  (-­‐a)

$  php  -­‐aInteractive  shell

php  >  echo  5+8;13php  >  function  addTwo($n)php  >  {php  {  return  $n  +  2;php  {  }php  >  var_dump(addtwo(2));int(4)php  >

Page 44: Cli the other SAPI confoo11

InteracOve  mode  (-­‐a)

$  php  -­‐aInteractive  shell

php  >  stri[TAB][TAB]strip_tags          stripcslashes    stripslashes      stristr                stripos                php  >  stri

Tab  comple^on

Page 45: Cli the other SAPI confoo11

Run  code  (-­‐r)

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

Page 46: Cli the other SAPI confoo11

Config  directory  (-­‐c)

$  php  -­‐c  /custom/dir/php.ini  script.php

Page 47: Cli the other SAPI confoo11

Define  custom  INI  seSng  (-­‐d)

$  php  -­‐d  max_execution_time=20  -­‐r  '$foo  =  ini_get("max_execution_time");  var_dump($foo);'string(2)  "20"$

Page 48: Cli the other SAPI confoo11

Get  INI  informaOon  (-­‐i)

$  php  -­‐i  |  grep  “log_”define_syslog_variables  =>  Off  =>  Offlog_errors  =>  On  =>  Onlog_errors_max_len  =>  1024  =>  1024$

Filtering  items

Page 49: Cli the other SAPI confoo11

Syntax/lint  check  (-­‐l)

$  php  -­‐l  myFile.phpNo  syntax  errors  detected  in  myFile.php$

Only  checks  parse  

errors

Page 50: Cli the other SAPI confoo11

Module  list  (-­‐m)

$  php  -­‐m[PHP  Modules]bcmathbz2calendarCorectypecurldatedba$

Page 51: Cli the other SAPI confoo11

Syntax  highlighOng  (-­‐s)

$  php  -­‐s  helloworld.php  >  helloworld.html$

<?phpecho  "Hello  world";

Page 52: Cli the other SAPI confoo11

Syntax  highlighOng  (-­‐s)

<?phpecho "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>

Page 53: Cli the other SAPI confoo11

Version  info  (-­‐v)

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

Page 54: Cli the other SAPI confoo11

FuncOon  reflecOon  (-­‐-­‐rf)

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

   -­‐  Parameters  [2]  {        Parameter  #0  [  <required>  $value  ]        Parameter  #1  [  <optional>  $options  ]    }}$

Page 55: Cli the other SAPI confoo11

Class  reflecOon  (-­‐-­‐rf)

$  php  -­‐-­‐rc  stdclassClass  [  <internal:Core>  class  stdClass  ]  {    -­‐  Constants  [0]  {    }    -­‐  Static  properties  [0]  {    }    -­‐  Static  methods  [0]  {    }    -­‐  Properties  [0]  {    }    -­‐  Methods  [0]  {    }}$

Page 56: Cli the other SAPI confoo11

Extension  reflecOon  (-­‐-­‐re)

$  php  -­‐-­‐re  jsonExtension  [  <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  ]            }        }...}

Page 57: Cli the other SAPI confoo11

Extension  INI  informaOon  (-­‐-­‐ri)

$  php  -­‐-­‐ri  pdo

PDO

PDO  support  =>  enabledPDO  drivers  =>  mysql,  sqlite,  sqlite2$

Page 58: Cli the other SAPI confoo11

Back  on  track

Page 59: Cli the other SAPI confoo11

Back  to  I/O

Page 60: Cli the other SAPI confoo11

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

Page 61: Cli the other SAPI confoo11

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

Page 62: Cli the other SAPI confoo11

STDIN

<?php

while(!feof(STDIN)){    $line = trim(fgets(STDIN));    if(strlen($line) > 0){        echo strrev($line).PHP_EOL;    }}

Page 63: Cli the other SAPI confoo11

STDIN

<?php

while(!feof(STDIN)){    $line = trim(fgets(STDIN));    if(strlen($line) > 0){        echo strrev($line).PHP_EOL;    }}

Stream  that  is  opened  by  default

Page 64: Cli the other SAPI confoo11

STDIN

$  php  -­‐r  "var_dump(STDIN);"resource(1)  of  type  (stream)$

The  proof  !

Stream  that  is  opened  by  default

Page 65: Cli the other SAPI confoo11

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

Page 66: Cli the other SAPI confoo11

Wordcount  example

$  cat  wordcount.txt  CanadaThijsCanadaThijsThijsConfoo$  cat  wordcount.txt    |  php  wordcount.php  Canada:  2Confoo:  1Thijs:  3$

Page 67: Cli the other SAPI confoo11

STDOUT

<?php$handle = fopen('php://stdout','w');fwrite($handle,'Hello world');fclose($handle);

STDOUT  ==  

echo

Page 68: Cli the other SAPI confoo11

STDOUT

<?phpfwrite(STDOUT,'Hello world');

Page 69: Cli the other SAPI confoo11

STDERR

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

Page 70: Cli the other SAPI confoo11

STDERR

<?phpfwrite(STDERR,'Serious error!');

Page 71: Cli the other SAPI confoo11

Mixing  STDOUT  &  STDERR

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

$  php  stdmix.php  STDOUT  outputSTDERR  output$

Page 72: Cli the other SAPI confoo11

Mixing  STDOUT  &  STDERR

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

$  php  stdmix.php  STDOUT  outputSTDERR  output$

Looks  the  same

Page 73: Cli the other SAPI confoo11

Mixing  STDOUT  &  STDERR

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

$  php  stdmix.php  &>    /dev/null$

Page 74: Cli the other SAPI confoo11

Mixing  STDOUT  &  STDERR

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

$  php  stdmix.php  &>    /dev/null$

STDOUT  is  caught

STDOUT  &  STDERR  are  

caught

Page 75: Cli the other SAPI confoo11

AlternaOve  output

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

Page 76: Cli the other SAPI confoo11

AlternaOve  output

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

echo  output  is  wriWen  

to  file

Page 77: Cli the other SAPI confoo11

Piping

$  php  -­‐r  'for($i=0;$i<10;$i++)  echo  $i.PHP_EOL;'0123456789$  php  -­‐r  'for($i=0;$i<10;$i++)  echo  $i.PHP_EOL;'  |  wc  -­‐l            10$

Page 78: Cli the other SAPI confoo11

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:  ThijsWhere  do  you  live:  Belgium

Hello  Thijs  from  Belgium$

Page 79: Cli the other SAPI confoo11

Shebang  !

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

$  chmod  +x  shebang.php$  ./shebang.phpHello  world$

Page 80: Cli the other SAPI confoo11

Encore

Page 81: Cli the other SAPI confoo11

PCNTL  according  to  php.net

Process  Control  support  in  PHP  implements  the  Unix  style  of  process  crea^on,  program  execu^on,  signal  handling  and  process  termina^on.

Page 82: Cli the other SAPI confoo11

Process  Control  should  not  be  enabled  within  a  web  server  environment  and  unexpected  results  may  happen  if  any  

Process  Control  func^ons  are  used  within  a  web  server  environment.

Page 83: Cli the other SAPI confoo11

Forking  according  to  Wikipedia

In  compu^ng,  when  a  process  forks,  it  creates  a  copy  of  itself

Page 84: Cli the other SAPI confoo11

Forking

<?php$pid = pcntl_fork();if ($pid == -1) {

//Forking failed} else if ($pid) {

//Parent logic} else {

//Child logic}

Copy  program  execu^on

PID  value  determines  context

PID  of  child  process

Page 85: Cli the other SAPI confoo11

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

Page 86: Cli the other SAPI confoo11

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  termina^on

Page 87: Cli the other SAPI confoo11

Signals

A  signal  is  a  limited  form  of  inter-­‐process  communica^on  used  in  Unix,  Unix-­‐like,  and  other  POSIX-­‐compliant  opera^ng  

systems.  Essen^ally  it  is  an  asynchronous  no^fica^on  sent  to  a  process  in  order  to  

no^fy  it  of  an  event  that  occurred.

Page 88: Cli the other SAPI confoo11

Signals

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

Page 89: Cli the other SAPI confoo11

Signals

<?phpdeclare(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  termina^on

Process  interrup^on

Catch  signals

Page 90: Cli the other SAPI confoo11

POSIX  process  control  funcOons

<?phpecho "[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);}

Page 91: Cli the other SAPI confoo11

POSIX  process  control  funcOons

<?phpecho "[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);}

Prefork  PID  ==  

parent  PID

Parent  PID  of  parent  ==  session  PID

parent  PID

child  PIDchild  PID

Page 92: Cli the other SAPI confoo11

POSIX  process  control  funcOons

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

Page 93: Cli the other SAPI confoo11

POSIX  process  control  funcOons

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

“KILL  0”  checks  existence

Send  SIGTERM  signal

Child  process  is  dead

Page 94: Cli the other SAPI confoo11

Some  PCNTL  guidelines

✓Detect  your  CWD

✓Use  PID  files

✓Control  you  file  privileges

✓Detach  you  session

✓Avoid  rogue  child  processes

✓Cleanup  your  garbage

Page 95: Cli the other SAPI confoo11

Jeroen  Keppens:  @jkeppenshttp://www.slideshare.net/jkeppens/php-­‐in-­‐the-­‐dark

Talk  dedicated  to  

process  control  in  PHP

Check  this  guy  out  !

Page 96: Cli the other SAPI confoo11
Page 97: Cli the other SAPI confoo11

Q&A

Page 98: Cli the other SAPI confoo11

Thanks  !