Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
CLI, the other SAPI (PHPDAY)
Search
Thijs Feryn
May 21, 2012
Technology
2
510
CLI, the other SAPI (PHPDAY)
The PHPDay edition of my "CLI, the other SAPI" talk.
Thijs Feryn
May 21, 2012
Tweet
Share
More Decks by Thijs Feryn
See All by Thijs Feryn
Caching the uncacheable with Varnish - PHP London 2020
thijsferyn
0
430
Accelerating OTT video platforms with Varnish - London Video Tech meetup 2020
thijsferyn
0
380
't Oncachebare cachen
thijsferyn
0
360
Caching the uncacheable with Varnish - PHP UG FFM 19
thijsferyn
1
720
Developing cacheable PHP applications - PHP Barcelona 2019
thijsferyn
0
650
Caching the uncacheable with Varnish - FullstackEU 2019
thijsferyn
0
470
Varnish beyond basic web acceleration - Symfony Live Berlin 2019
thijsferyn
0
390
Developing cacheable PHP applications
thijsferyn
0
420
Varnish beyond basic web acceleration - DAHO.AM 2019
thijsferyn
0
400
Other Decks in Technology
See All in Technology
Lakebaseを使ったAIエージェントを実装してみる
kameitomohiro
0
140
Tokyo_reInforce_2025_recap_iam_access_analyzer
hiashisan
0
190
自律的なスケーリング手法FASTにおけるVPoEとしてのアカウンタビリティ / dev-productivity-con-2025
yoshikiiida
2
17k
MobileActOsaka_250704.pdf
akaitadaaki
0
130
Reach American Airlines®️ Instantly: 19 Calling Methods for Fast Support in the USA
flyamerican
1
170
第4回Snowflake 金融ユーザー会 Snowflake summit recap
tamaoki
1
290
Lazy application authentication with Tailscale
bluehatbrit
0
220
2025-07-06 QGIS初級ハンズオン「はじめてのQGIS」
kou_kita
0
170
MUITにおける開発プロセスモダナイズの取り組みと開発生産性可視化の取り組みについて / Modernize the Development Process and Visualize Development Productivity at MUIT
muit
2
17k
Model Mondays S2E04: AI Developer Experiences
nitya
0
150
LangChain Interrupt & LangChain Ambassadors meetingレポート
os1ma
2
320
PO初心者が考えた ”POらしさ”
nb_rady
0
210
Featured
See All Featured
BBQ
matthewcrist
89
9.7k
Navigating Team Friction
lara
187
15k
4 Signs Your Business is Dying
shpigford
184
22k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
6
310
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
138
34k
The Language of Interfaces
destraynor
158
25k
Embracing the Ebb and Flow
colly
86
4.7k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Docker and Python
trallard
44
3.5k
Faster Mobile Websites
deanohume
307
31k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
970
Transcript
CLI,%the%other%SAPI Thijs%Feryn Evangelist +32%(0)9%218%79%06
[email protected]
php
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/6393
None
SAPI? The%way%you%interact%with%PHP
Common%SAPIs
Common%SAPIs • Apache/Apache%2 • FPM • FastCGI • ISAPI •
CLI • GTK
The%CLI%SAPI PHP%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
None
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 <?php echo "Number of arguments {$argc}\n";
foreach($argv as $key=>$argument){ echo "Argument # {$key}: {$argument}\n"; } Argument% count Argument% array
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
CLI 101 interpreting arguments $argc $argv $_SERVER[‘argc’] $_SERVER[‘argv’] !!! register_argc_argv
!!!
CLI 101 getopt <?php $arguments = getopt('ab:c::'); var_dump($arguments);
CLI 101 getopt <?php $arguments = getopt('ab:c::'); var_dump($arguments); OpWonal% value
Flag% (no%value) Required% value
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
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) ""["arg2"]=> ""string(3)""123" ""["arg3"]=> ""string(1)""x"
} Mind% the%“=”%sign
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 eerhT $ 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%interacWon • Sessions%&%cookies%as%workaround
• ExecuWon%Wmeouts • Limited%request/response%size
Comparing%the%Apache%&%CLI%SAPI CLI%SAPI • Controlable%state • Controlable%script%execuWon • ConWnuous%interacWon • No%need%for%sessions
• No%execuWon%Wmeouts
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 Interactive"shell php">"stri[TAB][TAB] strip_tags"""""stripcslashes"" stripslashes"""stristr"""""""" stripos"""""""" php">"stri Tab% 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"=>"Off log_errors"=>"On"=>"On log_errors_max_len"=>"1024"=>"1024 $ Filtering% items
Syntax/lint%check%("l) $"php":l"myFile.php No"syntax"errors"detected"in"myFile.php $ Only% checks%parse% errors
Module%list%("m) $"php":m [PHP"Modules] bcmath bz2 calendar Core ctype curl date
dba $
Syntax%highlighLng%("s) $"php":s"helloworld.php">"helloworld.html $ <?php echo""Hello"world";
Syntax%highlighLng%("s) <?php echo "Hello world"; <code><span style="color: #000000"> <span style="color:
#0000BB"><?php<br / ></span><span style="color: #007700">echo </span><span style="color: #DD0000">"Hello world"</span><span style="color: #007700">;</span> </span>
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 $
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
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.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
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
Back%on%track
Back%to%I/O
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 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
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" Italy"Thijs Thijs Italy Thijs"PHPDAY Thijs Italy $"cat"wordcount.txt""|"php"wordcount.php" PHPDAY:"1
Thijs:"4 Italy:"3 $
STDOUT <?php $handle = fopen('php://stdout','w'); fwrite($handle,'Hello world'); fclose($handle); STDOUT% ==%
echo
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 fwrite(STDOUT,'STDOUT output'.PHP_EOL); fwrite(STDERR,'STDERR output'.PHP_EOL); $"php"stdmix.php" STDOUT"output STDERR"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
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%wriqen% 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 $location\n"; $"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%funcWons%are%used%within% a%web%server%environment.
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
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% terminaWon
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); 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
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);
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
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 <?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
Jeroen%Keppens:%@jkeppens http://www.slideshare.net/jkeppens/phpYinYtheYdark Talk% dedicated%to% process%control% in%PHP Check%this% guy%out%!
None
Q&A
Thanks%!