$30 off During Our Annual Pro Sale. View details »

CLI, the other SAPI (PHPDAY)

CLI, the other SAPI (PHPDAY)

The PHPDay edition of my "CLI, the other SAPI" talk.

Thijs Feryn

May 21, 2012
Tweet

More Decks by Thijs Feryn

Other Decks in Technology

Transcript

  1. CLI,%the%other%SAPI Thijs%Feryn Evangelist +32%(0)9%218%79%06 thijs@combellgroup.com php

  2. About%me I’m%an%Evangelist%at%Combell

  3. About%me I’m%a%board%member%at%PHPBenelux

  4. Follow%me%on%Twi+er:%@ThijsFeryn Give%me%feedback:%h+p://joind.in/6393

  5. None
  6. SAPI? The%way%you%interact%with%PHP

  7. Common%SAPIs

  8. Common%SAPIs • Apache/Apache%2 • FPM • FastCGI • ISAPI •

    CLI • GTK
  9. The%CLI%SAPI PHP%script%execuWon%via%the%command%line%interface

  10. When%to%use

  11. When%to%use • In%crons • For%batch%tasks • For%worker%processes • Daemons •

    Process%control • InteracWon%with%other%binaries
  12. None
  13. CLI 101

  14. CLI 101 The PHP binary Passing arguments Reading from STDIN

    I/O with pipes
  15. CLI 101 Invoking a script with the PHP binary php#file.php

  16. CLI 101 Passing arguments php#file.php#arg1#arg2

  17. CLI 101 interpreting arguments <?php echo "Number of arguments {$argc}\n";

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

    foreach($argv as $key=>$argument){ echo "Argument # {$key}: {$argument}\n"; } Argument% count Argument% array
  19. CLI 101 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
  20. CLI 101 interpreting arguments $argc $argv $_SERVER[‘argc’] $_SERVER[‘argv’] !!! register_argc_argv

    !!!
  21. CLI 101 getopt <?php $arguments = getopt('ab:c::'); var_dump($arguments);

  22. CLI 101 getopt <?php $arguments = getopt('ab:c::'); var_dump($arguments); OpWonal% value

    Flag% (no%value) Required% value
  23. CLI 101 php"getopt.php":a":b"2":c3 array(3)"{ ""["a"]=> ""bool(false) ""["b"]=> ""string(1)""2" ""["c"]=> ""string(1)""3"

    } No% spacing%for% opWonal% arguments
  24. CLI 101 getopt: longopts <?php $arguments = getopt('',array('a rg1','arg2:','arg3::')); var_dump($arguments);

  25. CLI 101 php"getopt2.php"::arg1"::arg2"123"::arg3=x array(3)"{ ""["arg1"]=> ""bool(false) ""["arg2"]=> ""string(3)""123" ""["arg3"]=> ""string(1)""x"

    } Mind% the%“=”%sign
  26. 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);
  27. CLI 101 $"cat"test.txt"|"php"stdin.php" enO owT eerhT $

  28. CLI 101 $"cat"test.txt"|"php"stdin.php" enO owT eerhT $ Output% file Convert%

    output%to% input%with% pipes
  29. Comparing%the%Apache%&%CLI%SAPI

  30. Comparing%the%Apache%&%CLI%SAPI Web%based%SAPI’s • HTTP%is%a%stateless%protocol • Request/response%based • Limited%interacWon • Sessions%&%cookies%as%workaround

    • ExecuWon%Wmeouts • Limited%request/response%size
  31. Comparing%the%Apache%&%CLI%SAPI CLI%SAPI • Controlable%state • Controlable%script%execuWon • ConWnuous%interacWon • No%need%for%sessions

    • No%execuWon%Wmeouts
  32. The%PHP%binary php

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

  34. 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">
  35. InteracLve%mode%("a) $"php":a Interactive"shell php">"stri[TAB][TAB] strip_tags"""""stripcslashes"" stripslashes"""stristr"""""""" stripos"""""""" php">"stri Tab% compleWon

  36. Run%code%("r) $"php":r""echo"date('Y:m:d"H:i:s');" 2011:03:02"22:04:45 $

  37. Config%directory%("c) $"php":c"/custom/dir/php.ini"script.php

  38. Define%custom%INI%seRng%("d) $"php":d"max_execution_time=20":r"'$foo"=" ini_get("max_execution_time");" var_dump($foo);' string(2)""20" $

  39. Get%INI%informaLon%("i) $"php":i"|"grep"“log_” define_syslog_variables"=>"Off"=>"Off log_errors"=>"On"=>"On log_errors_max_len"=>"1024"=>"1024 $ Filtering% items

  40. Syntax/lint%check%("l) $"php":l"myFile.php No"syntax"errors"detected"in"myFile.php $ Only% checks%parse% errors

  41. Module%list%("m) $"php":m [PHP"Modules] bcmath bz2 calendar Core ctype curl date

    dba $
  42. Syntax%highlighLng%("s) $"php":s"helloworld.php">"helloworld.html $ <?php echo""Hello"world";

  43. Syntax%highlighLng%("s) <?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>
  44. 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 $

  45. FuncLon%reflecLon%(""rf) $"php"::rf"json_encode Function"["<internal:json>"function" json_encode"]"{ "":"Parameters"[2]"{ """"Parameter"#0"["<required>"$value"] """"Parameter"#1"["<optional>"$options"] ""} } $

  46. Class%reflecLon%(""rc) $"php"::rc"stdclass Class"["<internal:Core>"class"stdClass"]"{ "":"Constants"[0]"{ ""} "":"Static"properties"[0]"{ ""} "":"Static"methods"[0]"{ ""} "":"Properties"[0]"{

    ""} "":"Methods"[0]"{ ""} } $
  47. 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"] """"""}

    """"} ... }
  48. Extension%INI%informaLon%(""ri) $"php"::ri"pdo PDO PDO"support"=>"enabled PDO"drivers"=>"mysql,"sqlite,"sqlite2 $

  49. BuiltYin%webserver%("S) $"php":S"localhost:1234 PHP"5.4.0"Development"Server"started"at"Mon"May" 14"09:43:28"2012 Listening"on"localhost:1234 Document"root"is"/var/www/cli.dev Press"Ctrl:C"to"quit. $ PHP% 5.4

  50. BuiltYin%webserver%("S) [Mon"May"14"09:44:42"2012]"192.168.72.1:53147" [200]:"/ [Mon"May"14"09:44:42"2012]"192.168.72.1:53148" [200]:"/?=PHPE9568F34:D428:11d2:A769:00AA001ACF42 [Mon"May"14"09:44:42"2012]"192.168.72.1:53149" [200]:"/?=PHPE9568F35:D428:11d2:A769:00AA001ACF42

  51. BuiltYin%webserver%("S) $"php":S"localhost:1234"router.php PHP"5.4.0"Development"Server"started"at"Mon"May" 14"09:43:28"2012 Listening"on"localhost:1234 Document"root"is"/path/to/docroot Press"Ctrl:C"to"quit. $ Router

  52. BuiltYin%webserver%("t) $"php":S"localhost:1234":t"/path/to/docroot PHP"5.4.0"Development"Server"started"at"Mon"May" 14"09:43:28"2012 Listening"on"localhost:1234 Document"root"is"/path/to/docroot Press"Ctrl:C"to"quit. $ Custom% docroot

  53. Back%on%track

  54. Back%to%I/O

  55. Input%&%output Web • $_SERVER • $_GET • $_POST • $_COOKIE

    • $_SESSION • $_ENV CLI • $_SERVER • $argc/$argv • $_ENV • getopt() • STDIN/STDOUT/ STDERR
  56. Change%your%mindset

  57. Change%your%mindset Don’t%use%sessions%&%cookies Just%use%local%variables

  58. Change%your%mindset If%you%don’t%need%HTTP,%use%CLI Avoid%overhead E.g.% cronjobs

  59. Change%your%mindset Current%directory%!=%webroot ➡Use%dirname(__FILE__) ➡Use%chdir() ➡Use%getcwd() CLI% scripts%are% executable% everywhere

  60. 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);
  61. 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);
  62. STDIN <?php while(!feof(STDIN)){ $line = trim(fgets(STDIN)); if(strlen($line) > 0){ echo

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

    strrev($line).PHP_EOL; } } Stream% that%is%opened% by%default
  64. STDIN $"php":r""var_dump(STDIN);" resource(1)"of"type"(stream) $ The% proof%! Stream% that%is%opened% by%default

  65. 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; }
  66. Wordcount%example $"cat"wordcount.txt" Italy"Thijs Thijs Italy Thijs"PHPDAY Thijs Italy $"cat"wordcount.txt""|"php"wordcount.php" PHPDAY:"1

    Thijs:"4 Italy:"3 $
  67. STDOUT <?php $handle = fopen('php://stdout','w'); fwrite($handle,'Hello world'); fclose($handle); STDOUT% ==%

    echo
  68. STDOUT <?php fwrite(STDOUT,'Hello world');

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

  70. STDERR <?php fwrite(STDERR,'Serious error!');

  71. Mixing%STDOUT%&%STDERR <?php fwrite(STDOUT,'STDOUT output'.PHP_EOL); fwrite(STDERR,'STDERR output'.PHP_EOL); $"php"stdmix.php" STDOUT"output STDERR"output $

  72. Mixing%STDOUT%&%STDERR <?php fwrite(STDOUT,'STDOUT output'.PHP_EOL); fwrite(STDERR,'STDERR output'.PHP_EOL); $"php"stdmix.php" STDOUT"output STDERR"output $

    Looks% the%same
  73. Mixing%STDOUT%&%STDERR $"php"stdmix.php">"/dev/null" STDERR"output $ $"php"stdmix.php"&>""/dev/null $

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

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

    fclose($handle);
  76. AlternaLve%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
  77. 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 $
  78. 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 $
  79. Shebang%!

  80. Shebang%! #!/usr/bin/php <?php echo "Hello world".PHP_EOL; $"chmod"+x"shebang.php $"./shebang.php Hello"world $

  81. Encore

  82. Process%Control%should%not%be%enabled% within%a%web%server%environment%and% unexpected%results%may%happen%if%any% Process%Control%funcWons%are%used%within% a%web%server%environment.

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

    //Forking failed } else if ($pid) { //Parent logic } else { //Child logic } Copy% program% execuWon PID%value% determines% context PID%of%child% process
  84. 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; }
  85. 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% terminaWon
  86. 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);
  87. 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% terminaWon Process% interrupWon Catch%signals
  88. 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);
  89. 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().", paren t PID: ".posix_getppid().PHP_EOL; exit; } else { echo "[parent] PID: ".posix_getpid().", pare nt PID: ".posix_getppid().PHP_EOL; pcntl_wait($status); } Prefork%PID% ==% parent%PID Parent%PID% of%parent%==% session%PID parent% PID child%PID child%PID
  90. 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; }
  91. 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; } “KILL%0”%checks% existence Send%SIGTERM% signal Child%process% is%dead
  92. Jeroen%Keppens:%@jkeppens http://www.slideshare.net/jkeppens/phpYinYtheYdark Talk% dedicated%to% process%control% in%PHP Check%this% guy%out%!

  93. None
  94. Q&A

  95. Thanks%!