The PHPDay edition of my "CLI, the other SAPI" talk.
CLI,%the%other%SAPIThijs%FerynEvangelist+32%(0)9%218%79%06[email protected]php
View Slide
About%meI’m%an%Evangelist%at%Combell
About%meI’m%a%board%member%at%PHPBenelux
Follow%me%on%Twi+er:%@ThijsFerynGive%me%feedback:%h+p://joind.in/6393
SAPI?The%way%you%interact%with%PHP
Common%SAPIs
Common%SAPIs• Apache/Apache%2• FPM• FastCGI• ISAPI• CLI• GTK
The%CLI%SAPIPHP%script%execuWon%via%the%command%line%interface
When%to%use
When%to%use• In%crons• For%batch%tasks• For%worker%processes• Daemons• Process%control• InteracWon%with%other%binaries
CLI 101
CLI 101The PHP binaryPassing argumentsReading from STDINI/O with pipes
CLI 101Invoking a script with the PHPbinaryphp#file.php
CLI 101Passing argumentsphp#file.php#arg1#arg2
CLI 101interpreting argumentsecho "Number of arguments {$argc}\n";foreach($argv as $key=>$argument){echo "Argument # {$key}: {$argument}\n";}
CLI 101interpreting argumentsecho "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$arguments = getopt('ab:c::');var_dump($arguments);
CLI 101getopt$arguments = getopt('ab:c::');var_dump($arguments);OpWonal%valueFlag%(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%opWonal%arguments
CLI 101getopt: longopts$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$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%fileConvert%output%to%input%with%pipes
Comparing%the%Apache%&%CLI%SAPI
Comparing%the%Apache%&%CLI%SAPIWeb%based%SAPI’s• HTTP%is%a%stateless%protocol• Request/response%based• Limited%interacWon• Sessions%&%cookies%as%workaround• ExecuWon%Wmeouts• Limited%request/response%size
Comparing%the%Apache%&%CLI%SAPICLI%SAPI• Controlable%state• Controlable%script%execuWon• ConWnuous%interacWon• No%need%for%sessions• No%execuWon%Wmeouts
The%PHP%binaryphp
The%PHP%binaryUsage:"php"[options]"[:f]""[::]"[args...]"""""""php"[options]":r""[::]"[args...]"""""""php"[options]"[:B"]":R""[:E"]"[::]"[args...]"""""""php"[options]"[:B"]":F""[:E"]"[::]"[args...]"""""""php"[options]"::"[args...]"""""""php"[options]":a
InteracLve%mode%("a)$"php":aInteractive"shellphp">"echo"5+8;13php">"function"addTwo($n)php">"{php"{"return"$n"+"2;php"{"}php">"var_dump(addtwo(2));int(4)php">
InteracLve%mode%("a)$"php":aInteractive"shellphp">"stri[TAB][TAB]strip_tags"""""stripcslashes""stripslashes"""stristr""""""""stripos""""""""php">"striTab%compleWon
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)$"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%highlighLng%("s)$"php":s"helloworld.php">"helloworld.html$echo""Hello"world";
Syntax%highlighLng%("s)echo "Hello world";<?php>echo style="color:#DD0000">"Hello world"style="color: #007700">;
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$
FuncLon%reflecLon%(""rf)$"php"::rf"json_encodeFunction"[""function"json_encode"]"{"":"Parameters"[2]"{""""Parameter"#0"[""$value"]""""Parameter"#1"[""$options"]""}}$
Class%reflecLon%(""rc)$"php"::rc"stdclassClass"[""class"stdClass"]"{"":"Constants"[0]"{""}"":"Static"properties"[0]"{""}"":"Static"methods"[0]"{""}"":"Properties"[0]"{""}"":"Methods"[0]"{""}}$
Extension%reflecLon%(""re)$"php"::re"jsonExtension"[""extension"#20"json"version"1.2.1"]"{..."":"Functions"{""""Function"[""function"json_encode"]"{"""""":"Parameters"[2]"{""""""""Parameter"#0"[""$value"]""""""""Parameter"#1"[""$options"]""""""}""""}...}
Extension%INI%informaLon%(""ri)$"php"::ri"pdoPDOPDO"support"=>"enabledPDO"drivers"=>"mysql,"sqlite,"sqlite2$
BuiltYin%webserver%("S)$"php":S"localhost:1234PHP"5.4.0"Development"Server"started"at"Mon"May"14"09:43:28"2012Listening"on"localhost:1234Document"root"is"/var/www/cli.devPress"Ctrl:C"to"quit.$PHP%5.4
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
BuiltYin%webserver%("S)$"php":S"localhost:1234"router.phpPHP"5.4.0"Development"Server"started"at"Mon"May"14"09:43:28"2012Listening"on"localhost:1234Document"root"is"/path/to/docrootPress"Ctrl:C"to"quit.$Router
BuiltYin%webserver%("t)$"php":S"localhost:1234":t"/path/to/docrootPHP"5.4.0"Development"Server"started"at"Mon"May"14"09:43:28"2012Listening"on"localhost:1234Document"root"is"/path/to/docrootPress"Ctrl:C"to"quit.$Custom%docroot
Back%on%track
Back%to%I/O
Input%&%outputWeb• $_SERVER• $_GET• $_POST• $_COOKIE• $_SESSION• $_ENVCLI• $_SERVER• $argc/$argv• $_ENV• getopt()• STDIN/STDOUT/STDERR
Change%your%mindset
Change%your%mindsetDon’t%use%sessions%&%cookiesJust%use%local%variables
Change%your%mindsetIf%you%don’t%need%HTTP,%use%CLIAvoid%overheadE.g.%cronjobs
Change%your%mindsetCurrent%directory%!=%webroot➡Use%dirname(__FILE__)➡Use%chdir()➡Use%getcwd()CLI%scripts%are%executable%everywhere
STDIN$handle = fopen('php://stdin','r');while(!feof($handle)){$line = trim(fgets($handle));if(strlen($line) > 0){echo strrev($line).PHP_EOL;}}fclose($handle);
STDINwhile(!feof(STDIN)){$line = trim(fgets(STDIN));if(strlen($line) > 0){echo strrev($line).PHP_EOL;}}
STDINwhile(!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$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"Italy"ThijsThijsItalyThijs"PHPDAYThijsItaly$"cat"wordcount.txt""|"php"wordcount.php"PHPDAY:"1Thijs:"4Italy:"3$
STDOUT$handle = fopen('php://stdout','w');fwrite($handle,'Hello world');fclose($handle);STDOUT%==%echo
STDOUTfwrite(STDOUT,'Hello world');
STDERR$handle = fopen('php://stderr','w');fwrite($handle,'Serious error!');fclose($handle);
STDERRfwrite(STDERR,'Serious error!');
Mixing%STDOUT%&%STDERRfwrite(STDOUT,'STDOUT output'.PHP_EOL);fwrite(STDERR,'STDERR output'.PHP_EOL);$"php"stdmix.php"STDOUT"outputSTDERR"output$
Mixing%STDOUT%&%STDERRfwrite(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%caughtSTDOUT%&%STDERR%are%caught
AlternaLve%outputfclose(STDOUT);$handle = fopen(realpath(dirname(__FILE__).'/output.txt'),'a');echo "Hello world!".PHP_EOL;fclose($handle);
AlternaLve%outputfclose(STDOUT);$handle = fopen(realpath(dirname(__FILE__).'/output.txt'),'a');echo "Hello world!".PHP_EOL;fclose($handle);echo%output%is%wriqen%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$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:"BelgiumHello"Thijs"from"Belgium$
Shebang%!
Shebang%!#!/usr/bin/phpecho "Hello world".PHP_EOL;$"chmod"+x"shebang.php$"./shebang.phpHello"world$
Encore
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.
Forking$pid = pcntl_fork();if ($pid == -1) {//Forking failed} else if ($pid) {//Parent logic} else {//Child logic}Copy%program%execuWonPID%value%determines%contextPID%of%child%process
Forking$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$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%forkingWait%for%child%terminaWon
Signalsdeclare(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);
Signalsdeclare(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%terminaWonProcess%interrupWonCatch%signals
POSIX%process%control%funcLonsecho "[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%funcLonsecho "[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%PIDParent%PID%of%parent%==%session%PIDparent%PIDchild%PIDchild%PID
POSIX%process%control%funcLons$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$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%existenceSend%SIGTERM%signalChild%process%is%dead
Jeroen%Keppens:%@jkeppenshttp://www.slideshare.net/jkeppens/phpYinYtheYdarkTalk%dedicated%to%process%control%in%PHPCheck%this%guy%out%!
Q&A
Thanks%!