Brennen Bearnes
2020-08-28
My goal is to convey:
A whole lot of the web has been built on PHP.
An astonishing percentage of the web has been built on PHP.
Why? PHP totally sucks, right?
Probably, but:
foo.php
GET
request for https://www.example.com/foo.php
foo.php
foo.php
prints some stuff and exits, whatever it prints gets sent to the clientThe simplest example in PHP is just a file containing a string literal:
Output:
Hello world.
<?php
tag, the interpreter will just print everything it encounters.For a more traditional hello world, you’d write:
Output:
Hello world.
$
: $foo
, $bar
, $baz
$
, and conveys no type information (unlike Perl, etc.)[A-Za-z0-9_]
<?php
// Global:
$foo = 1;
// Local to function or method:
function bar() {
$foo = 2;
return $foo;
}
// Accessing a global within a function:
function baz() {
global $foo;
$foo = 3;
}
print "$foo\n";
print bar() . "\n";
baz();
print "$foo\n";
Output:
1
2
3
<?php
// Define a constant - all-caps by convention:
define('NAME', 'VALUE');
print NAME . "\n";
// Barewords can be risky since they're treated as string literals
// for undefined constants. You can use constant() instead:
print constant('NAME');
Output:
VALUE
VALUE
You’ll rarely encounter any but the first syntax for these, but they do work:
<?php
// Floating point:
$foo = 1.234;
$foo = 1.2e3;
$foo = 7E-10;
# $foo = 1_234.567;
// Integer:
$foo = 1;
$foo = 0123; // octal number (equivalent to 83 decimal)
$foo = 0x1A; // hexadecimal number (equivalent to 26 decimal)
$foo = 0b11111111; // binary number (equivalent to 255 decimal)
# $foo = 1_234_567; // decimal number (as of PHP 7.4.0)
<?php
$var = 'variable';
$double = "double-quoted with $var interpolation and escapes.\n";
$single = 'single-quoted';
// String concatenation, like in Perl:
print $double . $single;
Output:
double-quoted with variable interpolation and escapes.
single-quoted
An alternative string quoting mechanism, a la shell or Perl:
To avoid variable interpolation and escapes, enclose the end marker in single quotes:
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
)
<?php
// Mixing types is perfectly legal:
$foo = [ 'one', 2, 3.0 ];
// Shorthand for pushing onto end of array:
$foo[] = 'IV';
print_r( $foo );
Output:
Array
(
[0] => one
[1] => 2
[2] => 3
[3] => IV
)
<?php
$relenger_nicks = [
'dancy' => 'Ahmon Dancy',
'brennen' => 'Brennen Bearnes',
'liw' => 'Lars Wirzenius',
'longma' => 'Jeena Huneidi',
];
print_r($relenger_nicks);
Output:
Array
(
[dancy] => Ahmon Dancy
[brennen] => Brennen Bearnes
[liw] => Lars Wirzenius
[longma] => Jeena Huneidi
)
<?php
$relengers = [
[
'nick' => 'dancy',
'name' => 'Ahmon Dancy',
'editors' => [ 'emacs', 'vim' ]
],
[
'nick' => 'brennen',
'name' => 'Brennen Bearnes',
'editors' => [ 'vim', 'nano', 'edit.com' ]
],
];
print $relengers[1]['name'] . ' uses ' . $relengers[1]['editors'][0];
Output:
Brennen Bearnes uses vim
Implicit coercion is common, and (unsurprisingly) a frequent cause of bugs. (More about that in a minute.)
Types can be explicitly cast like so:
Aside from some edge cases, everything else should be truthy.
Which brings us to the type comparison table:
<?php
var_dump('monday' == 'tuesday');
var_dump('monday' == true);
var_dump('monday' == 1);
var_dump('monday' === true);
var_dump('monday' === 1);
Output:
bool(false)
bool(true)
bool(false)
bool(false)
bool(false)
if
and friends<?php
$day = 'monday';
if ( $day === 'monday' ) {
print "At least it's not Tuesday.\n";
} elseif ( $day === 'tuesday' ) {
print "Actually the worst day.\n";
} else if ( $day === 'wednesday' ) {
print "Looking up.\n";
} else {
print "Basically the weekend.\n";
}
Output:
At least it's not Tuesday.
if
and friends, short version<?php
$day = 'monday';
switch ( $day ) {
case 'monday':
print "At least it's not Tuesday.\n";
break;
case 'tuesday':
print "Actually the worst day.\n";
break;
case 'wednesday':
print "Looking up.\n";
break;
default:
print "Basically the weekend.\n";
}
Output:
At least it's not Tuesday.
Major caveat: Switch statements use loose type comparison.
C-style:
Output:
0123456789
More idiomatic than C-style iteration:
<?php
$stars = [ 1, 3, 7 ];
foreach ( $stars as $count ) {
print str_repeat( '*', $count ) . "\n";
}
Output:
*
***
*******
<?php
$relenger_nicks = [
'dancy' => 'Ahmon Dancy',
'brennen' => 'Brennen Bearnes',
'liw' => 'Lars Wirzenius',
'longma' => 'Jeena Huneidi',
];
foreach ( $relenger_nicks as $nick => $name ) {
print "$name goes by $nick on IRC\n";
}
Output:
Ahmon Dancy goes by dancy on IRC
Brennen Bearnes goes by brennen on IRC
Lars Wirzenius goes by liw on IRC
Jeena Huneidi goes by longma on IRC
<?php
while ( true ) {
print "In loop.\n";
break;
}
print "Out of loop.\n";
// Far less common, but supported.
do {
print "In loop.\n";
break;
} while ( true );
print "Out of loop.\n";
Output:
In loop.
Out of loop.
In loop.
Out of loop.
<?php
function meow( $count ) {
$ret = '';
for ( $i = 0; $i < $count; $i++ ) {
$ret .= "meow\n";
}
return $ret;
}
echo meow( 5 );
Output:
meow
meow
meow
meow
meow
We can specify parameter types and default values:
<?php
function meow( int $count = 1 ) {
$ret = '';
for ( $i = 0; $i < $count; $i++ ) {
$ret .= "meow\n";
}
return $ret;
}
echo meow( 5.0 );
Output:
meow
meow
meow
meow
meow
…but there’s a catch. Unless strict typing mode is on, this will just result in casting $count
to an integer.
<?php
declare( strict_types = 1 );
function meow( int $count = 1 ) {
$ret = '';
for ( $i = 0; $i < $count; $i++ ) {
$ret .= "meow\n";
}
return $ret;
}
echo meow( 5.0 );
Output:
PHP Fatal error: Uncaught TypeError: Argument 1 passed to meow() must be of the type int, float given, called in Standard input code on line 11 and defined in Standard input code:3
Stack trace:
#0 Standard input code(11): meow(5)
#1 {main}
thrown in Standard input code on line 3
<?php
function times_ten (int &$value) {
$value = $value * 10;
}
$value = 10;
times_ten($value);
var_dump($value);
Output:
int(100)
<?php
abstract class Encabulator {
abstract public function reticulate();
}
class TurboEncabulator extends Encabulator {
protected $_splinesReticulated = false;
public function reticulate() {
$this->_splinesReticulated = true;
}
public function splineStatus() {
return $this->_splinesReticulated ? 'reticulated' : 'unreticulated';
}
}
$te = new TurboEncabulator();
$te->reticulate();
print "Spline status: " . $te->splineStatus();
Output:
Spline status: reticulated
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(),
__isset(), __unset(), __sleep(), __wakeup(), __serialize(),
__unserialize(), __toString(), __invoke(), __set_state(), __clone() and
__debugInfo()
<?php
class ValueStash {
private $_values;
public function __construct( array $values ) {
$this->_values = $values;
}
public function __get( $name ) {
return $this->_values[$name];
}
public function __set ( $name, $value ) {
$this->_values[ $name ] = $value;
}
}
$editors = new ValueStash( [ 'brennen' => 'vim' ] );
print $editors->brennen;
Output:
vim
With a basic overview of the language out of the way, let’s go over some common pitfalls and sources of recurring bugs.
Output:
PHP Notice: Undefined index: fo in /tmp/phpslideoutput.php on line 7
Notice: Undefined index: fo in /tmp/phpslideoutput.php on line 7
NULL
Advice: Check with array_key_exists($key, $array)
or otherwise guard array values.
This comes up constantly.
<?php
$foo = new stdClass;
// Look, a typo:
$fo->some_method();
Output:
PHP Notice: Undefined variable: fo in Standard input code on line 3
Notice: Undefined variable: fo in Standard input code on line 3
PHP Fatal error: Uncaught Error: Call to a member function some_method() on null in Standard input code:3
Stack trace:
#0 {main}
thrown in Standard input code on line 3
isset()
, and is_null()
<?php
var_dump(isset($undefined));
var_dump(is_null($undefined));
$defined = null;
var_dump(isset($defined));
Output:
bool(false)
PHP Notice: Undefined variable: undefined in /tmp/phpslideoutput.php on line 4
Notice: Undefined variable: undefined in /tmp/phpslideoutput.php on line 4
bool(true)
bool(false)