Stupid PHP Tricks

Stupid PHP Tricks

PHP is an incredibly flexible language, with an interesting array of built-in functionality. In this talk, I’ll show you some of my favorite and hidden features in PHP. Learn how to abuse output buffering, the [] operator, and three ways to limit how long a function can execute.

Presented on November 14, 2014 at php[world].

B455534f8023a559d267c5874021fdef?s=128

John Bafford

November 14, 2014
Tweet

Transcript

  1. © 2014 John Bafford php[world] 2014 — 11/14/2014 “Stupid PHP

    Tricks” John Bafford! http://bafford.com — @jbafford — john@bafford.com ! The Brick Factory! http://thebrickfactory.com — @thebrickfactory — jbafford@thebrickfactory.com 1
  2. © 2014 John Bafford php[world] 2014 — 11/13/2014 Who Am

    I? • John Bafford
 Vice President, Programming Services
 The Brick Factory • PHP developer since 1999 • Programmer since 1990 2
  3. © 2014 John Bafford php[world] 2014 — 11/13/2014 The Brick

    Factory • Full Service Web Development Shop • Farragut North, DC • We do lots of Drupal sites 3
  4. © 2014 John Bafford php[world] 2014 — 11/13/2014 Output Buffers

    4
  5. © 2014 John Bafford php[world] 2014 — 11/13/2014 Pop Quiz

    function a(){ echo 'Hello'; echo 'World'; } function b(){ echo 'Hello', 'World'; } function c(){ echo 'Hello' . 'World'; } 5
  6. function a(){ echo 'Hello'; echo 'World'; } ! line #

    * op fetch ext return operands! ---------------------------------------------------------------------------------! 3 0 > ECHO 'Hello'! 1 ECHO 'World'! 2 > RETURN null! ! ! ! function b(){ echo 'Hello', 'World'; } ! line # * op fetch ext return operands! ---------------------------------------------------------------------------------! 4 0 > ECHO 'Hello'! 1 ECHO 'World'! 2 > RETURN null! ! ! ! function c(){ echo 'Hello' . 'World'; } ! line # * op fetch ext return operands! ---------------------------------------------------------------------------------! 5 0 > CONCAT ~0 'Hello', 'World'! 1 ECHO ~0! 2 > RETURN null!
  7. function a(){ echo 'Hello'; echo 'World'; } ! line #

    * op fetch ext return operands! ---------------------------------------------------------------------------------! 3 0 > ECHO 'Hello'! 1 ECHO 'World'! 2 > RETURN null! ! ! ! function b(){ echo 'Hello', 'World'; } ! line # * op fetch ext return operands! ---------------------------------------------------------------------------------! 4 0 > ECHO 'Hello'! 1 ECHO 'World'! 2 > RETURN null! ! ! ! function c(){ echo 'Hello' . 'World'; } ! line # * op fetch ext return operands! ---------------------------------------------------------------------------------! 5 0 > CONCAT ~0 'Hello', 'World'! 1 ECHO ~0! 2 > RETURN null!
  8. function a(){ echo 'Hello'; echo 'World'; } function b(){ echo

    'Hello', 'World'; } function c(){ echo 'Hello' . 'World'; } function obwrap($fn) { ob_start(function($s) { return $s . $s; }, 1); $fn(); ob_end_clean(); } $ob = false; for($x = 1; $x < $argc; $x++) { $fn = $argv[$x]; if($fn == ‘--ob') { $ob = true; continue; } echo "$fn:\n"; if($ob) obwrap($fn); else $fn(); echo "\n\n"; } php st-ob.php a b c! a:! HelloWorld! ! b:! HelloWorld! ! c:! HelloWorld!
  9. function a(){ echo 'Hello'; echo 'World'; } function b(){ echo

    'Hello', 'World'; } function c(){ echo 'Hello' . 'World'; } function obwrap($fn) { ob_start(function($s) { return $s . $s; }, 1); $fn(); ob_end_clean(); } $ob = false; for($x = 1; $x < $argc; $x++) { $fn = $argv[$x]; if($fn == ‘--ob') { $ob = true; continue; } echo "$fn:\n"; if($ob) obwrap($fn); else $fn(); echo "\n\n"; } php st-ob.php a b c! a:! HelloWorld! ! b:! HelloWorld! ! c:! HelloWorld! php st-ob.php --ob a b c! a:! HelloHelloWorldWorld! ! b:! HelloHelloWorldWorld! ! c:! HelloWorldHelloWorld!
  10. php st-ob.php --ob a b c! a:! HelloHelloWorldWorld! ! b:!

    HelloHelloWorldWorld! ! c:! HelloWorldHelloWorld!
  11. chunk_size If the optional parameter chunk_size is passed, the buffer

    will be flushed after any output call which causes the buffer's length to equal or exceed chunk_size. The default value 0 means that the output function will only be called when the output buffer is closed. ! Prior to PHP 5.4.0, the value 1 was a special case value that set the chunk size to 4096 bytes. bool ob_start ( [ callable $output_callback = NULL [, int $chunk_size = 0 [, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS ]]] )
  12. function a(){ echo 'Hello'; echo 'World'; } function b(){ echo

    'Hello', 'World'; } function c(){ echo 'Hello' . 'World'; } function obwrap($fn) { ob_start(function($s) { return $s . $s; }, 1); $fn(); ob_end_clean(); } $ob = false; for($x = 1; $x < $argc; $x++) { $fn = $argv[$x]; if($fn == ‘--ob') { $ob = true; continue; } echo "$fn:\n"; if($ob) obwrap($fn); else $fn(); echo "\n\n"; } php st-ob.php a b c! a:! HelloWorld! ! b:! HelloWorld! ! c:! HelloWorld! php st-ob.php --ob a b c! a:! HelloHelloWorldWorld! ! b:! HelloHelloWorldWorld! ! c:! HelloWorldHelloWorld!
  13. © 2014 John Bafford php[world] 2014 — 11/13/2014 http_build_query 13

  14. Generates a URL-encoded query string from the associative (or indexed)

    array provided. string http_build_query ( mixed $query_data [, string $numeric_prefix [, string $arg_separator [, int $enc_type = PHP_QUERY_RFC1738 ]]] )
  15. © 2014 John Bafford php[world] 2014 — 11/13/2014 Please Don’t

    Write This $url = "https://your.favorite.api/favoritizer/ json?api_key=%s&api_secret= %s&param_one=value&param_two=%s&%message=%s"; $encoded = urlencode($some_message) $url = sprintf($url, $api_key, $api_secret, 'value', $param_two, $encoded); 15
  16. © 2014 John Bafford php[world] 2014 — 11/13/2014 Write This

    Instead $url = "https://your.favorite.api/favoritizer/ json?" . http_build_query([ 'api_key' => $api_key, 'api_secret' => $api_secret, 'param_one' => 'value', 'param_two' => $param_two, 'message' => $some_message, ]); 16
  17. © 2014 John Bafford php[world] 2014 — 11/13/2014 [] 17

  18. $array = []; $array[] = 'foo'; $array[] = 'bar'; print_r($array);

    /* Array ( [0] => foo [1] => bar ) */
  19. $array = []; $array[] = 'foo'; $array[] = 'bar'; print_r($array);

    /* Array ( [0] => foo [1] => bar ) */ ! ! $x = $array[]; /* Fatal error: Cannot use [] for reading in …/phpworld/StupidPHPTricks/array.php on line 124 */
  20. $array = []; $array[] = 'foo'; $array[] = 'bar'; print_r($array);

    /* Array ( [0] => foo [1] => bar ) */ ! ! $foo = 'foo'; $bar = &$foo; var_dump($foo, $bar); /* string(3) "foo" string(3) "foo" */ ! ! ! $bar = 'bar'; var_dump($foo, $bar); /* string(3) "bar" string(3) "bar" */
  21. $array = []; $array[] = 'foo'; $array[] = 'bar'; print_r($array);

    /* Array ( [0] => foo [1] => bar ) */ $array[] = &$foo; $foo = 'baz'; print_r($array); /* Array ( [0] => foo [1] => bar [2] => baz ) */
  22. $array = []; $array[] = 'foo'; $array[] = 'bar'; !

    ! $baz = ['foo', 'bar']; $array[] = &$baz; $baz[] = 'baz'; print_r($array); /* Array ( [0] => foo [1] => bar [2] => Array ( [0] => foo [1] => bar [2] => baz ) ) */
  23. function setGreek(&$output) { $output = ['α', 'β', 'ɣ', 'δ']; }

    ! $x = null; setGreek($x); print_r($x); ! /* Array ( [0] => α [1] => β [2] => ɣ [3] => δ ) */
  24. function setGreek(&$output) { $output = ['α', 'β', 'ɣ', 'δ']; }

    ! ! setGreek($x); print_r($x); ! /* Array ( [0] => α [1] => β [2] => ɣ [3] => δ ) */
  25. function setGreek(&$output) { $output = ['α', 'β', 'ɣ', 'δ']; }

    ! $values = [ ['1', '2', '3'], ['a', 'b', 'c'], ]; ! setGreek($values[]); print_r($values); /* Array ( [0] => Array ( [0] => 1 [1] => 2 [2] => 3 ) [1] => Array ( [0] => a [1] => b [2] => c ) [2] => Array ( [0] => α [1] => β [2] => ɣ [3] => δ ) ) */
  26. $a = [1, 2, 3]; $b = &$a[]; $c =

    &$a[]; $a[] = 'a'; $b = 'b'; $c = 'c'; print_r($a); /* Array ( [0] => 1 [1] => 2 [2] => 3 [3] => b [4] => c [5] => a ) */
  27. © 2014 John Bafford php[world] 2014 — 11/13/2014 Practical Application

    of &$arr[] 27
  28. $xml = simplexml_load_string(file_get_contents('xml.xml')); $array = json_decode(json_encode((array) $xml), true); $array =

    array($xml->getName() => $array);
  29. function _parse($xml) { $_level = array(); $_cdata = ''; $_data

    = array(); while($xml->read()) { switch($xml->nodeType) { case XMLReader::ELEMENT: $_cdata = ''; $name = $xml->name; $curLevel = count($_level); if($curLevel == 0) //the root $_level[] = &$_data[$name][]; else $_level[] = &$_level[$curLevel - 1][$name][]; //Fall-through and close the tag if it is empty if(!$xml->isEmptyElement) break; case XMLReader::END_ELEMENT: if($_cdata != '') { $curLevel = count($_level) - 1; $_level[$curLevel] = $_cdata; $_cdata = ''; } array_pop($_level); break; case XMLReader::TEXT: case XMLReader::CDATA: $_cdata .= $xml->value; break; } } return $_data; }
  30. <?xml version="1.0" encoding="UTF-8"?> <foo> <bar>one</bar> <bar>two</bar> <baz>three</baz> <baz><quux>four</quux></baz> </foo> Array(!

    [foo] => Array(! [0] => Array(! [bar] => Array(! [0] => one! [1] => two! )! ! [baz] => Array(! [0] => three! [1] => Array(! [quux] => Array(! [0] => four! )! )! )! )! )! )
  31. © 2014 John Bafford php[world] 2014 — 11/13/2014 array_combine 31

  32. Creates an array by using the values from the keys

    array as keys and the values from the values array as the corresponding values. array array_combine ( array $keys , array $values )
  33. © 2014 John Bafford php[world] 2014 — 11/13/2014 array_combine $fd

    = fopen('somefile.csv', 'r'); $cols = null; while(false !== ($row = fgetcsv($fd))) { if(!$cols) $cols = $row; else if(count($row) == count($cols)) { $row = array_combine($cols, $row); print_r($row); } } 33 A,B,C! 1,2,3! 4,5,6 Array! (! [A] => 1! [B] => 2! [C] => 3! )! Array! (! [A] => 4! [B] => 5! [C] => 6! )!
  34. © 2014 John Bafford php[world] 2014 — 11/13/2014 (unset) 34

  35. © 2014 John Bafford php[world] 2014 — 11/13/2014 Unset cast

    • Returns null • That’s it. 35
  36. $foo = 'foo'; $bar = (unset)$foo; $foo === 'foo'; $bar

    === null;
  37. © 2014 John Bafford php[world] 2014 — 11/13/2014 WHY?? 37

  38. $bar = ( $foo ? FuncA() : (unset)FuncB() );

  39. $bar = ( $foo ? FuncA() : (unset)FuncB() ); if($foo)

    { $bar = FuncA(); } else { FuncB(); $bar = null; }
  40. © 2014 John Bafford php[world] 2014 — 11/13/2014 Controlling Runaway

    Functions 40
  41. © 2014 John Bafford php[world] 2014 — 11/13/2014 Controlling Runaway

    Functions • PHP has limited multithreading support • There’s the pthreads pecl module, but it’s unlikely you have --enable-maintainer-zts enabled 41
  42. A tick is an event that occurs for every N

    low-level tickable statements executed by the parser within the declare block. The value for N is specified using ticks=N within the declare block's directive section. declare(ticks = 1);
  43. © 2014 John Bafford php[world] 2014 — 11/13/2014 Method 1

    SIGALRM 43
  44. class LimitExecutionAlarm { function __construct($time, $fn) { $this->limit = $time;

    $this->fn = $fn; } function run() { pcntl_signal(SIGALRM, function() { echo “HALT!\n"; exit; }); pcntl_alarm($this->limit); $fn = $this->fn; $fn(); echo "Stopped.\n"; } } function slowFunc() { for($x = 0;; $x++) { echo "Iteration $x\n"; sleep(1); } } $slow = new LimitExecutionAlarm(
 10, ‘slowFunc' ); $slow->run();
  45. © 2014 John Bafford php[world] 2014 — 11/13/2014 Method 2

    Modify the Original Function 45
  46. class LimitExecutionThrottleable { function __construct($time, $fn) { $this->limit = $time;

    $this->fn = $fn; } function run() { $halt = false; pcntl_signal(SIGALRM, function() use(&$halt) { $halt = true; }); pcntl_alarm($this->limit); $fn = $this->fn; $fn($halt); echo "Stopped.\n"; } } function slowFuncThrottleable(&$halt = false) { for($x = 0;; $x++) { echo "Iteration $x\n"; sleep(1); if($halt) { echo "halt requested\n"; break; } } } $slow = new LimitExecutionThrottleable( 10, ‘slowFuncThrottleable' ); $slow->run();
  47. © 2014 John Bafford php[world] 2014 — 11/13/2014 Method 3

    fork 47
  48. class LimitExecutionFork { function run() { $pid = pcntl_fork(); if($pid

    == -1) { //error } else if($pid) { //parent echo "Forked $pid\n"; $done = false; pcntl_signal(SIGALRM, function() use($pid) { if(!$done) { posix_kill($pid, SIGTERM); echo "Killed $pid.\n”; } }); pcntl_alarm($this->limit); $status = false; $child = pcntl_wait($status); $done = true; } else { //child $fn = $this->fn; $fn(); exit; } } } function slowFunc() { for($x = 0;; $x++) { echo "Iteration $x\n"; sleep(1); } } $slow = new LimitExecutionFork( 10, ‘slowFunc' ); $slow->run();
  49. © 2014 John Bafford php[world] 2014 — 11/14/2014 https://joind.in/talk/view/11915 !

    John Bafford! http://bafford.com — @jbafford — john@bafford.com ! The Brick Factory! http://thebrickfactory.com — @thebrickfactory — jbafford@thebrickfactory.com Questions? 49
  50. © 2014 John Bafford php[world] 2014 — 11/14/2014 https://joind.in/talk/view/11915 !

    John Bafford! http://bafford.com — @jbafford — john@bafford.com ! The Brick Factory! http://thebrickfactory.com — @thebrickfactory — jbafford@thebrickfactory.com Thank You! 50