Cli the other SAPI confoo11

Post on 15-Jan-2015

3.257 views 2 download

Tags:

description

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

Transcript of Cli the other SAPI confoo11

CLI,  the  other  SAPI

Thijs  FerynEvangelist+32  (0)9  218  79  06thijs@combellgroup.com

ConfooThursday  March  10th  2011Montreal,  Canada

php

About  me

I’m  an  Evangelist  at  Combell

About  me

I’m  a  board  member  at  PHPBenelux

I  live  in  the  wonderful  city  of  Bruges

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

Follow  me  on  Twi+er:  @ThijsFeryn

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

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

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”

SAPI  according  to  Wikipedia

The  way  you  interact  with  PHP

Common  SAPIs

• Apache/Apache  2

• CGI

• FastCGI

• ISAPI

• CLI

• GTK

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

The  CLI  SAPI  according  to  php.net

PHP  script  execu^on  via  the  command  line  interface

When  to  use

• In  crons

• For  batch  tasks

• For  worker  processes

• Daemons

• Process  control

• Interac^on  with  other  binaries

CLI 101

CLI 101

The PHP binary

Passing arguments

Reading from STDIN

I/O with pipes

CLI 101Invoking a script with the

PHP binary

php  file.php

CLI 101Passing arguments

php  file.php  arg1  arg2

CLI 101interpreting arguments

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

CLI 101interpreting arguments

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

Argument  countArgument  

array

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

CLI 101interpreting arguments

$argc

$argv

$_SERVER[‘argc’]

$_SERVER[‘argv’]

!!! register_argc_argv !!!

CLI 101getopt

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

CLI 101getopt

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

Op^onal  value

Flag  (no  value)

Required  value

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

CLI 101getopt: longopts

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

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

Mind  the  “=”  sign

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

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

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

Output  file

Convert  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  interac^on

• Sessions  &  cookies  as  workaround

• Execu^on  ^meouts

• Limited  request/response  size

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

Measuring  the  state

Input  &  output

Input  &  output

Web• $_SERVER

• $_GET

• $_POST

• $_COOKIE

• $_SESSION

• $_ENV

CLI• $_SERVER

• $argc/$argv

• $_ENV

• getopt()

• STDIN/STDOUT/STDERR

Change  your  mindset

Change  your  mindset

Don’t  use  sessions  &  cookies

Just  use  local  variables

Change  your  mindset

Don’t  “get”  your  input

Just  “read”  it

Change  your  mindset

Don’t  bundle  your  output

You  can  use  distributed  output

Change  your  mindset

If  you  don’t  need  HTTP,  use  CLI

Avoid  overhead

E.g.  cronjobs

Change  your  mindset

Current  directory  !=  webroot

➡Use  dirname(__FILE__)

➡Use  chdir()

➡Use  getcwd()

CLI  scripts  are  executable  everywhere

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

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  >

InteracOve  mode  (-­‐a)

$  php  -­‐aInteractive  shell

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

Tab  comple^on

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  seSng  (-­‐d)

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

Get  INI  informaOon  (-­‐i)

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

Filtering  items

Syntax/lint  check  (-­‐l)

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

Only  checks  parse  

errors

Module  list  (-­‐m)

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

Syntax  highlighOng  (-­‐s)

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

<?phpecho  "Hello  world";

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>

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$

FuncOon  reflecOon  (-­‐-­‐rf)

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

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

Class  reflecOon  (-­‐-­‐rf)

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

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  ]            }        }...}

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

$  php  -­‐-­‐ri  pdo

PDO

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

Back  on  track

Back  to  I/O

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

<?php

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

Stream  that  is  opened  by  default

STDIN

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

The  proof  !

Stream  that  is  opened  by  default

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  CanadaThijsCanadaThijsThijsConfoo$  cat  wordcount.txt    |  php  wordcount.php  Canada:  2Confoo:  1Thijs:  3$

STDOUT

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

STDOUT  ==  

echo

STDOUT

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

STDERR

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

STDERR

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

Mixing  STDOUT  &  STDERR

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

$  php  stdmix.php  STDOUT  outputSTDERR  output$

Mixing  STDOUT  &  STDERR

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

$  php  stdmix.php  STDOUT  outputSTDERR  output$

Looks  the  same

Mixing  STDOUT  &  STDERR

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

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

Mixing  STDOUT  &  STDERR

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

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

STDOUT  is  caught

STDOUT  &  STDERR  are  

caught

AlternaOve  output

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

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

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$

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$

Shebang  !

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

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

Encore

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.

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.

Forking  according  to  Wikipedia

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

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

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

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

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.

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

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

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

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

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

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

Some  PCNTL  guidelines

✓Detect  your  CWD

✓Use  PID  files

✓Control  you  file  privileges

✓Detach  you  session

✓Avoid  rogue  child  processes

✓Cleanup  your  garbage

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

Talk  dedicated  to  

process  control  in  PHP

Check  this  guy  out  !

Q&A

Thanks  !