Skip to content

Latest commit

 

History

History
2362 lines (2173 loc) · 58.7 KB

php-i-may-2023.md

File metadata and controls

2362 lines (2173 loc) · 58.7 KB

PHP-I May 2023

NOTE TO SELF:

  • Post the entire block of course code to the download folder
    • Send link via email to attendees
  • Add instructions to install phpMyAdmin in the VM

Homework

For Wed 24 May 2023

For Mon 22 May 2023

For Fri 19 May 2023

For Wed 17 May 2023

sudo dpkg --configure -a
sudo apt -y update && sudo apt -f -y install && sudo apt -y full-upgrade
  • Apache reconfig:
sudo apt-add-repository ppa:ondrej/apache2
sudo apt install libapache2-mod-php8.2
sudo a2dismod php8.0
sudo systemctl restart apache2
sudo a2enmod php8.2
sudo systemctl restart apache2

Class Notes

Unicode characters:

// any unicode character
echo "\u{0252} \u{0282}";

Mixing HTML and PHP

<?php
// mixing HTML and PHP: need the closing tag
$first = 'Fred';
$last  = 'Flintstone';
$status = 'Caveman';
?>
<h1>Name</h1>
<hr />
<!-- Standard PHP tag -->
<?php echo $first . ' ' . $last; ?>
<br />
<!-- Implied echo; same result -->
<?= $first . ' ' . $last; ?>

Handling file uploads

Different formats

<?php
// all of these === 13
$a = 0b1101; // binary (base 2) // only in PHP 8.1
$b = 015;	 // octal (base 8)
$c = 0x0D; 	 // hex (base 16)
$d = 13;	 // decimal (base 10)
var_dump($a, $b, $c, $d);

NOTE: money_format() has been replaced by NumberFormatter::formatCurrency

Error log for the VM:

cat /var/log/apache2/error.log

sort() vs asort()

<?php
// NOTE: sort() wipes out the original keys!
$astronaut = ['firstName' => 'Mark', 'lastName' => 'Watney',
        'Specialty' => 'Botanist'];
sort($astronaut);
print_r($astronaut);

// output:
/*
Array
(
    [0] => Botanist
    [1] => Mark
    [2] => Watney
)
*/

// Use "asort()" to retain key/value relationships
$astronaut = ['firstName' => 'Mark', 'lastName' => 'Watney',
        'Specialty' => 'Botanist'];
asort($astronaut);
print_r($astronaut);

// output:
/*
Array
(
    [Specialty] => Botanist
    [firstName] => Mark
    [lastName] => Watney
)
*/

Example of getting 1st and last characters using substr()

<?php
// NOTE: substr($str, -1) gives you the *last* character
//       substr($str, 0, 1) gives you the *first* character
$dir = __DIR__ . '/';
$fn  = 'test.php';
if (substr($dir, -1) === DIRECTORY_SEPARATOR) {
	$path = $dir . $fn;
} else {
	$path = $dir . DIRECTORY_SEPARATOR . $fn;
}
echo $path;

Example reading a web page using file_get_contents()

<?php
$contents = file_get_contents('https://google.com/');
$contents = str_ireplace('Google', 'Boogle', $contents);
echo $contents;

Resetting the error log:

$filename = '/some/path/error.log';
ini_set('error_log', $filename);

glob()

  • Returns full path file info
  • Accepts a "filter" for filenames scandir()
  • Returns all files in a given directory
  • Filenames only, not full path
<?php
$list = [
	'glob' => glob(__DIR__ . '/*'),
	'scan' => scandir(__DIR__),
];

var_dump($list);

NOTE: glob() and scandir() are not recursive

Foundation

Comments

Docblocks

Operators

  • Back tics
    • Makes your code less portable
    • OS specific
    • Costly in terms of resource usage
    • Here's an example of using back tics but making it more portable
<?php
if (PHP_OS_FAMILY === 'Windows') {
	echo `dir *.*`;
} else {
	echo `ls -l`;
}

  • Modulus
<?php
$minutes = 149;
echo $minutes
	 . ' minutes is '
	 . ((int) ($minutes / 60))
	 . ' hours and '
	 . ($minutes % 60)
	 . ' minutes'
	 . PHP_EOL;
// Output: 149 minutes is 2 hours and 29 minutes

Concatenate operator and order or precedence is different between PHP 7 and PHP 8

<?php

$foo = 5;
$bar = 10;

// In PHP 8: line 8 is interpreted exactly as in line 11
// In PHP 7: line 8 is interpreted exactly as in line 14
echo "sum: " . $foo + $bar;

// Math operations have higher precedence
echo "sum: " . ($foo + $bar);

// Evaluated left-to-right results fatal
echo ("sum: " . $foo) + $bar;

Adding to arrays:

<?php

$astronaut = ['Mark', 'Watney', 'Botanist'];
$astronaut[] = 'ID101';

print_r($astronaut); // Array(0 => Mark, 1 => Watney, 2 => Botanist, 3 => ID101)

$astronaut = ['firstName' => 'Mark', 'LastName' => 'Watney', 'Specialty' => 'botanist'];
$astronaut['ID'] = 'ID101';

print_r($astronaut); // Array(firstName => Mark, lastName => Watney, Specialty => Botanist, ID => ID101)
echo $astronaut['ID']; // ID101

// actual output:
/*
Array
(
    [0] => Mark
    [1] => Watney
    [2] => Botanist
    [3] => ID101
)
Array
(
    [firstName] => Mark
    [LastName] => Watney
    [Specialty] => botanist
    [ID] => ID101
)
ID101
*/

Using the "spread" (variadics) operator to "flatten" two arrays into one

  • NOTE: you could also just do this: $days = array_combine($a, $b);
<?php
$a = ['Mon','Tue','Wed','Thu'];
$b = ['Fri','Sat','Sun'];

// do something

$days = [$a, $b];
var_dump($days);

// output
/*
 * array(2) {
  [0]=>
  array(4) {
    [0]=>
    string(3) "Mon"
    [1]=>
    string(3) "Tue"
    [2]=>
    string(3) "Wed"
    [3]=>
    string(3) "Thu"
  }
  [1]=>
  array(3) {
    [0]=>
    string(3) "Fri"
    [1]=>
    string(3) "Sat"
    [2]=>
    string(3) "Sun"
  }
}
*/


$days = [...$a, ...$b];
var_dump($days);
 /*
  * array(7) {
  [0]=>
  string(3) "Mon"
  [1]=>
  string(3) "Tue"
  [2]=>
  string(3) "Wed"
  [3]=>
  string(3) "Thu"
  [4]=>
  string(3) "Fri"
  [5]=>
  string(3) "Sat"
  [6]=>
  string(3) "Sun"
}
*/

Variables / Constants

Example of assigning constants:

A better way of assigning sub-arrays:

<?php
// Build the crew
$mission = [
	'STS395' => [
		['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
		['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
		['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist'],
	]
];

// Output all elements
print_r($mission);

Control Structures

Example if / elseif / else

<?php
$a = 30;
$b = 20;

if ($a < $b) {
	$result = 'less than';
} elseif( $a === $b) {
	$result = 'equal to';
} else {
	$result = 'greater than';
}
echo $a . ' is ' . $result . ' ' . $b . '.';
echo PHP_EOL;

An alternate "if" syntax is available:

<?php
$foo = 10;
$bar = 10;
if ( $foo > $bar ) :
    echo "Foo is greater than bar";
elseif ($foo < $bar) :
    echo "Foo is less than bar";
else :
    echo "Foo is equal to bar";
endif;

Example of the ternary operator extracting URL params

<?php
// URL: http://sandbox/first.php?name=Aurelio&location=Dallas
$name = (isset($_GET['name'])) ? strip_tags($_GET['name']) : 'Default';
$location = (isset($_GET['location'])) ? strip_tags($_GET['location']) : 'Some City';
echo htmlspecialchars($name . ' lives in ' . $location);

Another ternary example

<?php
$name = (isset($_GET['name'])) ? strip_tags($_GET['name']) : 'Default';
$age  = (isset($_GET['age']))  ? (int) $_GET['age']        : 0;
$city = (isset($_GET['city'])) ? strip_tags($_GET['city']) : 'Unknown';

echo '<pre>';
echo '<ul>';
echo '<li>' . 'Name: ' . htmlspecialchars($name) . '</li>';
echo '<li>' . 'Age : ' . htmlspecialchars($age) . '</li>';
echo '<li>' . 'City: ' . htmlspecialchars($city) . '</li>';
echo '</ul>';
echo '</pre>';

phpinfo(INFO_VARIABLES);

match also supports a default value:

<?php
// match does a *strict* comparison
// the example below returns "None of the above"
$result = match ('1') {
    0 => 'Foo',
    1 => 'Bar',
    default => 'None of the above'
};
echo $result . PHP_EOL;

Expanded example from homework:

<?php
$launch = array('Booster'    =>'Checked',
			    'Telemetry'  =>'Checked',
			    'Control'    =>'Bad',
				'Electrical' =>'Checked',
				'Network'    =>'Checked',
				'Fuel'       =>'Low');
$expected = count($launch);
$actual   = 0;
$problem  = '';
foreach($launch as $checklist => $status) {
    if ($status === 'Checked') {
		echo "$checklist is a go <br>\n";
		$actual++;
	} else {
		$problem .= $checklist . ':' . $status . PHP_EOL;
	}
}
if ($actual === $expected) {
	echo "Launch is a GO!<br />\n";
} else {
	echo "Launch aborted<br />\n";
	echo $problem;
}

do/while example

<?php
$items = array('CPU'  => 600,
			   'case' => 100,
			   'fan'  => 25,
			   'brush' => 0,
			   'GPU'  => 500 );
if (empty($items)) {
	echo "Sorry, no items!";
	exit;
}
reset($items);	// moves pointer to start
do {
	$item = key($items);
	$value = current($items);
	echo  "$item = $" . $value *1.85 .  "<br>\n";
} while (next($items) !== FALSE);

IMPORTANT: each() was removed in PHP 8.1!!!

VM Notes

Info

  • Username: vagrant
  • Password: vagrant

Update everything!

  • Open a terminal window and run this command:
sudo apt update
sudo apt -y upgrade
  • NOTE: this task might take some time
  • Do not update the version of Ubuntu: leave it at 20.04

Install phpMyAdmin

  • Download the latest version from https://www.phpmyadmin.net
  • Make note of the version number (e.g. 5.2.1)
cd
VER=5.2.1
unzip Downloads/phpMyAdmin-$VER-all-languages.zip
sudo cp -r phpMyAdmin-$VER-all-languages/* /usr/share/phpmyadmin
sudo cp /usr/share/phpmyadmin/config.sample.inc.php /usr/share/phpmyadmin/config.inc.php
  • Create the "blowfish secret"
sudo -i
export SECRET=`php -r "echo md5(date('Y-m-d-H-i-s') . rand(1000,9999));"`
echo "\$cfg['blowfish_secret']='$SECRET';" >> /usr/share/phpmyadmin/config.inc.php
exit

Set permissions

sudo chown -R www-data /usr/share/phpmyadmin

Snapshot

  • Be sure to take a snapshot of the VM before you start any of the labs!

System Problem

  • If you see this message: System program problem detected
  • Do this:
sudo rm -r /var/crash*

Class Notes

PHP Roadmap:

An alternative way to run PHP is in "async" mode

Request/Response

/home/vagrant/Zend/workspaces/DefaultWorkspace/sandbox/public
  • To access from that directory:
http://sandbox/NAME_OF_PROGRAM.php

Array example:

$user = [
	'first_name' => 'Fred',
	'last_name'  => 'Flintstone',
	'email'      => '[email protected]',
	'address'    => [
		'steet_num'   => 123,
		'street_name' => 'Main St',
		'city'        => 'Dallas',
		'state'       => 'TX',
	],
];

Example mixing HTML and PHP:

<?php
$a = ['A','B','C'];
?>
<ul>
<?php foreach ($a as $item) { ?>
<li><?= $item ?></li>
<?php } ?>
</ul>

You can use type coercion as a form of security

<?php
// reads a URL parameter "id"
$id = $_GET['id'] ?? 0;
$id = (int) $id;	// any scripting is removed
echo 'ID: ' . $id;

Formal "docblock" specification:

Attributes can be used in PHP 8 in place of docblocks

<?php
/**
 * Adds two integers
 *
 * @param int $a
 * @param int $b
 * @return int $result
 */
function add(int $a, int $b) : int
{
	return $a + $b;
}

echo add(2,2);
echo "\n";

#[description("Adds two integers") ]
#[int(a) ]
#[int(b) ]
#[returns(a - b)]
function sub(int $a, int $b) : int
{
	return $a - $b;
}

echo sub(2,2);
echo "\n";

You can also use words for logicals:

// you can use words instead of symbols:
$foo = 10;
$bar = 5;
echo ($foo == 10 and $bar == 5); // 1

$foo = 5;
$bar = 10;
echo ($foo != $bar or $foo > $bar); // 1
echo ($foo != $bar xor $foo > $bar); // 1

Recommended: use shell_exec() instead of back tics

<?php
// this will go away:
echo `ls -lha`;

// recommended
echo shell_exec('ls -lha');

You can use and, or and xor instead of symbols:

<?php
$foo = 10;
$bar = 5;
echo ($foo == 10 and $bar == 5); // 1

$foo = 5;
$bar = 10;
echo ($foo != $bar or $foo > $bar); // 1
echo ($foo != $bar xor $foo > $bar); // 1

Flattening or "unpacking" arrays:

<?php
$abc = ['A','B','C'];
$def = ['D','E','F'];
// this ends up with 2 element, each a sub-array
$foo = [$abc, $def];
// this "flattens" the two arrays and you end with
// a single 1 dimensioned array
$bar = [...$abc, ...$def];
var_dump($foo, $bar);

"Packing" an array by using the variadics operator as in the function signature

<?php
// Argument packing
$foo = 10;
$bar = 5;
$baz = 99;

// this use of the variadics operator
// has the effect of "packing" the array
function sum(...$args){
	// if you allow for an unlimited # arguments
	// you need to write you function to account for that
    return array_sum($args);
}
echo sum($foo, $bar, $baz, 9999); // 15

Arrays auto-assign indices as the next highest value. The order of the indices has no bearing on the order elements are stored. Elements are stored in the order received.

<?php
$a[1] = 'A';
$a[3] = 'B';
$a[2] = 'C';
$a[6] = 'D';
$a[]  = 'E';
$a[4] = 'F';
$a[]  = 'G';

var_dump($a);

// output:
/*
home/vagrant/Zend/workspaces/DefaultWorkspace/sandbox/public/test.php:10:
array(7) {
  [1] =>
  string(1) "A"
  [3] =>
  string(1) "B"
  [2] =>
  string(1) "C"
  [6] =>
  string(1) "D"
  [7] =>
  string(1) "E"
  [4] =>
  string(1) "F"
  [8] =>
  string(1) "G"
}
 */

When assigning multi-dimensional arrays, if the values are known in advance, use this style:

<?php
// Build the crew
$mission = [
	'STS395' => [
		['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
		['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
		['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist'],
	]
];

// Output all elements
print_r($mission);

Adding a new element to a crew member:

<?php
$mission = [
    'STS395' => [
        ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
        ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
        ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist']
    ]
];

// add new crew member
$mission['STS395'][] = ['firstName' => 'Lada', 'lastName' => '--', 'specialty' => 'My Favorite Dog'];
// change specialty
$mission['STS395'][1]['specialty'] = 'Senior Commander';
// adds an "id" element to the 1st sub-array
$mission['STS395'][1]['id'] = 101;

var_dump($mission);

When rendering numeric values, PHP defaults to decimal (i.e. base 10) If you want other formats, use one of these options:

  • NumberFormatter class
  • number_format() function
<?php
function formatCurrency(float $value, string $arg='$') {
    switch (strtolower($arg)) {
        case 'euro' :
        case '€' :
            $result = number_format($value, 2, ',', ' ') . '€';
            break;
        case 'pound' :
        case '£' :
            $result = '£' . number_format($value, 2, '.', ',');
            break;
        case 'dollar' :
        case '$' :
            $result = '$' . number_format($value, 2, '.', ',');
            break;
    }
    return $result;
}
echo formatCurrency(9999, 'euro') ?? 'Unknown'; // 9 999, €
echo PHP_EOL;
echo formatCurrency(9999, '$') ?? 'Unknown';    // $9,999.
  • printf() family of functions (uses a format string)

Use round() (out of the "Math" extension) to round values up or down

<?php
$items = [10.41, 15.96 , 30.99, 46.99];
var_dump($items);

// Add an 8% markup
foreach ($items as $key => $value) {
    //store the new value back into the array
    $items[$key] = round($value * 1.08, 2);
    echo $items[$key] . '<br>' . PHP_EOL;
}

var_dump($items);

Loops

Using a foreach() loop to iterate through a 3-level array

<?php
// Build the crew
$mission = [
    'STS395' => [
        ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
        ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
        ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist']
    ]
];

foreach($mission as $mission_id => $crew) {
	echo $mission_id . ':' . PHP_EOL;
	foreach ($crew as $num => $astronaut) {
		echo $num . ':' . PHP_EOL;
		foreach ($astronaut as $key => $value) {
			echo $key . "\t" . $value . PHP_EOL;
		}
	}
}

Using a while() loop to execute based upon a time condition

<?php
$counter = 0;
$current = time();
$stop  = time() + 2;	// 2 seconds
while ($current <= $stop){
	$current = time();
	echo $counter++ . ' ';
}

HTTP Basics

All incoming data is suspect

  • Filter validate and sanitize all suspect data
  • Escape suspect data upon output
echo htmlspecialchars($name);
  • Usually the web server is configured to recognize PHP in certain directories
    • In the VM: the config files are here:
/etc/apache2/sites-available
/etc/apache2/sites-enabled

Control Structures

Example of ternary operator

<?php
$name = (isset($_GET['name'])) ? strip_tags($_GET['name']) : 'Unknown';
echo $name;

Use of null coalesce operator vs. ternary

<?php
// null coalesce operator
$id = $_GET['id'] ?? $_POST['id'] ?? $_SESSION['id'] ?? $_COOKIE['id'] ?? 0;

// same thing with nested ternary ops:
// in PHP 8 use of parentheses are mandatory
// NOT recommended!
$id = ((!empty($_GET['id']))
	? $_GET['id']
	: ((!empty($_POST['id']))
		? $_POST['id']
		: ((!empty($_SESSION['id']))
			? $_SESSION['id']
			: 0)));

Match expression with a default:

<?php
$result = match ('1') {
    0 => 'Foo',
    1 => 'Bar',
    default => 'Unknown'
};

echo $result; // Unknown

Example of nested foreach() loops

<?php
$mission = [
    'STS395' => [
        ['firstName' => 'Fred', 'lastName' => 'Flintstone', 'specialty' => 'Caveman'],
        ['firstName' => 'Barney', 'lastName' => 'Rubble', 'specialty' => 'Caveman Assistant'],
    ],
    'STS396' => [
        ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
        ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
        ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist'],
    ],
];

foreach ($mission as $key => $value) {
	echo "Mission: $key\n";
	foreach ($value as $i => $entry) {
		echo $entry['firstName'] . ' ' . $entry['lastName'] . "\n";
	}
}

Example of unpacking an array into individual variables:

<?php
$mission = [
    'STS395' => [
        ['firstName' => 'Fred', 'lastName' => 'Flintstone', 'specialty' => 'Caveman'],
        ['firstName' => 'Barney', 'lastName' => 'Rubble', 'specialty' => 'Caveman Assistant'],
    ],
    'STS396' => [
        ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
        ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
        ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist'],
    ],
];

foreach ($mission as $key => $value) {
	echo "Mission: $key\n";
	foreach ($value as $i => list('firstName' => $first, 'lastName' => $last)) {
		echo $first . ' ' . $last . "\n";
	}
}

foreach ($mission as $key => $value) {
	echo "Mission: $key\n";
	foreach ($value as $i => $entry) {
		extract($entry);
		echo $firstName . ' ' . $lastName . "\n";
	}
}


$mission = [
    'STS395' => [
        ['Fred', 'Flintstone', 'Caveman'],
        ['Barney', 'Rubble', 'Caveman Assistant'],
    ],
    'STS396' => [
        ['Mark', 'Watney', 'Botanist'],
        ['Melissa', 'Lewis', 'Commander'],
        ['Beth', 'Johanssen', 'Computer Specialist'],
    ],
];

// unpack a numeric array in the foreach() directly
foreach ($mission as $key => $value) {
	echo "Mission: $key\n";
	foreach ($value as $i => list($first, $last, $specialty)) {
		echo "$first $last is a $specialty\n";
	}
}

// unpack a numeric array inside the foreach() loop
foreach ($mission as $key => $value) {
	echo "Mission: $key\n";
	foreach ($value as $i => $entry) {
		[$first, $last, $specialty] = $entry;
		echo "$first $last is a $specialty\n";
	}
}

Once the objective has been achieved: exit the loop. In this example, once an 'ERROR' has been found, we're done!

<?php
$messages = [
	'Operation succeeded',
	'ERROR 402',
	'Parse ERROR',
	'Everything OK',
];

$found = 0;
$search = 'ERROR';
foreach ($messages as $item) {
    // "str_contains()" is only available in PHP 8!
	if (str_contains($item, $search)) {
		$found++;
		break;
	}
}
echo ($found)
	? 'ERROR found'
	: 'All OK';
echo "\n";

You should provide a data type hint for functions with components that are sensitive to the wrong data type

  • Protects the function from abuse
  • Makes the real source of the error quite clear
function searchForError(array $messages) : int
{
	$found = 0;
	$search = 'ERROR';
	foreach ($messages as $item) {
		if (str_contains($item, $search)) {
			$found++;
			break;
		}
	}
	return $found;
}
$messages = [
	'Operation succeeded',
	'ERROR 402',
	'Parse ERROR',
	'Everything OK',
];

echo (searchForError('WHATEVER'))
	? 'ERROR found'
	: 'All OK';

Use declare(strict_types=1) to enforce all type hints for that file

<?php
// if the following line is omitted, the type-hint acts like a filter (type-cast)
declare(strict_types=1);
// Example of function using "type hinting"
function add(int $a, int $b) : int
{
	return $a + $b;
}

echo "The sum of 2 and 2 is " . add(2, 2) . "\n";
echo "The sum of 33.33 and 22.22 is " . add(33.33, 22.22) . "\n";

Nullable type: ?string === string|null

<?php
// union types were introduced in PHP 8

function get_full_name(string $first, string $last, string|null $middle = NULL)
{
	return ($middle) ? "$first $middle $last\n" : "$first $last\n";
}

echo get_full_name('Fred', 'Flintstone', 'John');
echo get_full_name('Barney', 'Rubble');

// prior to PHP 8, a hybrid type:
// ?string === string|null

function get_full_name2(string $first, string $last, ?string $middle = NULL)
{
	return ($middle) ? "$first $middle $last\n" : "$first $last\n";
}

echo get_full_name2('Fred', 'Flintstone', 'John');
echo get_full_name2('Barney', 'Rubble');

Union types can go overboard:

<?php
// a bit ridiculous:
function dump(int|float|string|bool|array|object $whatever)
{
	var_dump($whatever);
}

dump(new ArrayObject());
dump([1,2,3,4,5]);

// this makes more sense:
function dump2(mixed $whatever)
{
	var_dump($whatever);
}

dump(new ArrayObject());

// another example of ridiculous:
// dump(true|false|bool $yesNo) {}

Array navigation functions example with while() loop

<?php
$invoiceItems = [
  ['invoiceNumber' => 123, 'invoiceAmount' => 100],
  ['invoiceNumber' => 124, 'invoiceAmount' => 50],
  ['invoiceNumber' => 125, 'invoiceAmount' => 150],
  ['invoiceNumber' => 126, 'invoiceAmount' => 55],
];

$tax = 0.10;

while ($items = current($invoiceItems)) {
    $amountWithTax =  $items['invoiceAmount'] + ($items['invoiceAmount'] * $tax);
    echo 'invoice #' . $items['invoiceNumber'] . ' with invoice amount ' . $items['invoiceAmount'] . ' has the final amount of ' .  $amountWithTax . ' after adding the tax';
    echo "\n";
    next($invoiceItems);
}

You can also assign a reference to a single array element

<?php
$mission = [
    'STS395' => [
        ['firstName' => 'Fred', 'lastName' => 'Flintstone', 'specialty' => 'Caveman'],
        ['firstName' => 'Barney', 'lastName' => 'Rubble', 'specialty' => 'Caveman Assistant'],
    ],
    'STS396' => [
        ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
        ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
        ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist'],
    ],
];

$name = &$mission['STS395'][1]['firstName'];
$name = 'Betty';

var_dump($mission);

Example using pass-by-reference for validation

<?php
function validate(array $data, string &$err_msg) : bool
{
	$error = 0;
	// checks for only alpha characters
	if (!ctype_alpha($data['name'])) {
		$err_msg .= "Only letters are allowed in the name\n";
		$error++;
	}
	if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
		$err_msg .= "Invalid email address\n";
		$error++;
	}
	return ($error === 0);
}

$data = [
	'name' => 12345,
	'email' => 'bad.email.address'
];
$message = '';
if (validate($data, $message)) {
	echo "All OK\n";
} else {
	echo $message;
}

Example using list()

<?php
$purchases = [
	['product' => 'Candy Bar', 'date' => '4-12-23', 'price' => 1.50],
	['product' => 'Chips', 'date' => '4-16-23', 'price' => .75],
	['product' => 'Pop', 'date' => '4-12-23', 'price' => 1.00],
	['product' => 'Frozen Food - Pizza', 'date' => '4-21-23', 'price' => 2.75],
	['product' => 'Hot Food - Sandwhich', 'date' => '4-21-23', 'price' => 4.75],
	['product' => 'Ice Cream', 'date' => '4-18-23', 'price' => 2.50],
	['product' => 'Sneakers', 'date' => '5-18-23', 'price' => 2.50]
];


$i = 0;
$month = '4';

do
{
	if (strpos($purchases[$i]['date'], $month) === 0)  {
		list($prod, $date, $price) = array_values($purchases[$i]);
		echo "<b>Product sold:</b> $prod\t$price\t$date\n";
	}
    $i++;
}
while($i < count($purchases));

Calling program for the Forms demo in VM:

<?php
// place this calling program into:
// /home/vagrant/Zend/workspaces/DefaultWorkspace/sandbox/public/form.php
// call from a browser: http://sandbox/form.php
$config = include __DIR__ . '/../../orderapp/config/config.php';
include __DIR__ . '/../../orderapp/src/Forms.php';
echo getForm($config, 'new_order', NULL);

Example of vprintf + printf()

<?php
$a = 5398;
printf('%016b', $a);
echo "\n";

$data =	[
	['Fred', 999.99, 'Caveman'],
	['Wilma', 888.88, 'Cavewoman'],
];

foreach ($data as $row)
	vprintf('Name: %12s : Amount %8.2f : Title: %12s' . "\n", $row);

Example of using substr() to extract a filename extension

<?php
$fn = 'whatever.php';
$allowed = ['jpg', 'png', 'gif'];
$ext = substr(trim($fn), -3);
echo (in_array($ext, $allowed)) ? 'Allowed' : 'Denied';
echo "\n";
// comes back as "Denied" because the extension is not on the allowed list

Sanitizing a filename

<?php
$alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
echo $alpha[0] . $alpha[2] . $alpha[4];

$path = '/home/vagrant//Zend/workspaces/DefaultWorkspace/sandbox/public';
$fn   = 'test.php';
// alternative syntax:
// if ($path[-1] === '/') {
if ($path[strlen($path) - 1] === '/') {
	$final = $path . $fn;
} else {
	$final = $path . '/' . $fn;
}
echo str_replace('//', '/', $final) . "\n";

Example of a callback tree that produces output in different formats

  • Uses anonymous functions
<?php
$arr = ['A' => 111,'B' => 222,'C' => 333];

$callbacks = [
	// arrow function works well here
	'json' => fn(array $data) => json_encode($data, JSON_PRETTY_PRINT),
	// needs multiple lines of code, so we use an anonymous function
	'html' => function (array $data) {
		$out = '<table>';
		foreach ($data as $key => $value)
			$out .= '<tr><th>' . $key . '</th><td>' . $value . '</td></tr>';
		$out .= '</table>';
		return $out; }
];

echo $callbacks['json']($arr);
echo "\n";
echo $callbacks['html']($arr);
echo "\n";

I/O

Example using fopen() and fgetcsv() to read a data file

<?php
// data source: https://download.geonames.org/export/dump/countryInfo.txt
$fn = '/home/vagrant/Downloads/countryInfo.txt';
$fh = fopen($fn, 'r');
$data = [];
while (!feof($fh)) {
	$temp = fgetcsv($fh, separator:"\t");
	if (empty($temp) || $temp[0][0] === '#') continue;
	$data[] = $temp;
}
var_dump($data);

Example accessing a remote website

<?php
$contents = file_get_contents('https://google.com');
$contents = str_ireplace('Google', 'Boogle', $contents);
echo $contents;

Example using file_get_contents() to post form data

<?php
$target   = 'http://' . $host . '/ch12/php8_chat_ajax.php';
$response = 'Default';
if ($_POST) {
    $user = $_POST['from'] ?? '';
    $_SESSION['user'] = $user;
    $headers = [
        'Accept: text/html',
        'Content-type: application/x-www-form-urlencoded',
    ];
    $opts = [
        'http' => [
            'method'  => 'POST',
            'header'  => implode("\r\n", $headers),
            'content' => http_build_query($_POST)
        ]
    ];
    $context = stream_context_create($opts);
    $response = file_get_contents($target, FALSE, $context);
    $data = json_decode($response, TRUE);
}

Example from labs

<?php
$name = 'data.txt';
$textArray = ['Some ', 'text', 'abc', 'jiofsjij'];
$file = fopen($name, 'w+');
foreach($textArray as $text) {
  fwrite($file, $text . "\n");
}
rewind($file);
// another approach
echo substr(fread($file, 4096), 2, 2);
fclose($file);
$contents = file($name);
var_dump($contents);

Getting a list of files in a directory

<?php
// single directory
$path = __DIR__;
$list = glob($path . '/*');
foreach ($list as $fn) echo $fn . "\n";

// or grab an entire directory tree
// see: https://php.net/SPL
$iter = new RecursiveDirectoryIterator($path);
$all  = new RecursiveIteratorIterator($iter);
// $obj === SplFileInfo instance
foreach ($all as $fn => $obj) echo $fn . "\n";

PHP Packages

Database Operations

Basic query example

<?php
$conn = mysqli_connect('localhost', 'vagrant', 'vagrant', 'phpcourse');
$result = mysqli_query($conn, 'SELECT * FROM customers');
$num_rows = mysqli_row_count($result);	// especially useful for INSERT, UPDATE and DELETE
// gives results 1 row at a time
// use this if you anticipate a large result set
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
	var_dump($row);
}


// gives you all rows at once
// use this is expected return is no more than 1000 to 2000 rows
// $data = mysqli_fetch_all($result, MYSQLI_ASSOC);

Example drawn from the course materials

<?php
function saveOrder($conn, array $data) {
    // Build a query
    $query = vsprintf('INSERT INTO orders '
					  . '(date,status,amount,description,customer) '
					  . "VALUES ('%d', '%s', '%d', '%s', '%s')", $data);

    // Execute the query returning boolean true on success
    return mysqli_query($conn, $query);
}

function getOrders($conn) {
    // Build a query
    $query = 'SELECT * FROM orders';

    // Execute the query returning boolean true on success
    $result = mysqli_query($conn, $query);
    $data = [];
    while ($row = mysqli_fetch_assoc($result)) {
		$data[] = $row;
	}
	return $data;
}

$config = [
	'db' => [
        'dsn' => '127.0.0.1',
        'username' => 'vagrant',
        'password' => 'vagrant',
        'database' => 'phpcourse'
    ],
];

$conn = mysqli_connect($config['db']['dsn'], $config['db']['username'], $config['db']['password'], $config['db']['database']);
$data = [time(), 'open', 400, 'office chair', 3];
echo saveOrder($conn, $data) ? 'Data saved' : 'Data not saved';
$list = getOrders($conn);
foreach ($list as $row)
	vprintf("%d \t %s \t %d \t %s \t %s \n", $row);

Example of an SQL JOIN:

mysql> select * from customers as c join orders as o on c.id = o.customer;
+----+-----------+-----------+----+------------+----------+--------+--------------------------+----------+
| id | firstname | lastname  | id | date       | status   | amount | description              | customer |
+----+-----------+-----------+----+------------+----------+--------+--------------------------+----------+
|  4 | Susan     | Chu       |  1 | 1355097600 | complete |    560 |                          |        4 |
|  3 | J         | Flores    |  2 | 1359062345 | invoiced |   9800 |                          |        3 |
|  2 | Janet     | Levitz    |  3 | 1357948800 | held     |    300 |                          |        2 |
|  3 | J         | Flores    |  4 | 1359500400 | open     |     34 | Paper                    |        3 |
|  1 | George    | Stevenson |  5 | 1359586800 | open     |   4570 | PHP development          |        1 |
|  5 | Thomas    | White     |  6 | 1359586800 | invoiced |   2000 | Laptop                   |        5 |
|  3 | J         | Flores    |  7 | 1360796400 | open     |    300 | A big box of chocolates. |        3 |
|  3 | J         | Flores    |  8 | 1685102423 | open     |    400 | office chair             |        3 |
|  3 | J         | Flores    |  9 | 1685102435 | open     |    400 | office chair             |        3 |
+----+-----------+-----------+----+------------+----------+--------+--------------------------+----------+
9 rows in set (0.00 sec)

Miscellaneous

Highly recommended JavaScript library

<?php
$a = [
	9,		// decimal
	0b1001,	// binary
	0o11,	// octal
	011,	// octal
	0x09,	// hexadecimal
];

foreach ($a as $num) echo $num . PHP_EOL;

Basic

  • Testing for TRUE/FALSE
<?php
$offline = 1;
$status = (empty($offline)) ? 'ONLINE' : 'OFFLINE';
echo "The system is $status\n";
$status = ($offline === 0) ? 'ONLINE' : 'OFFLINE';
echo "The system is $status\n";
$status = ($offline) ? 'ONLINE' : 'OFFLINE';
echo "The system is $status\n";
  • Using objects to store multiple properties
<?php
class CaveMan
{
	// PHP 7 syntax
	public string $first;
	public string $last;
	public function __construct(string $first, string $last)
	{
		$this->first = $first;
		$this->last  = $last;
	}
	// PHP 8 syntax
	/*
	public function __construct(
		public string $first,
		public string $last) {}
	*/
}
$whatever[] = new CaveMan('Fred','Flintstone');
$whatever[] = new CaveMan('Wilma','Flintstone');
$whatever[] = new CaveMan('Barney','Rubble');
$whatever[] = new CaveMan('Betty','Rubble');
var_dump($whatever);

  • Unicode escape characters
<?php
// see: https://unicode-table.com/en/sets/top-emoji/
$emoji = "\u{1F602}"
	   . "\u{1F60D}"
	   . "\u{1F923}"
	   . "\u{1F60A}"
	   . "\u{1F60E}"
	   . "\u{1F606}"
	   . "\u{1F601}"
	   . "\u{1F609}"
	   . "\u{1F914}"
	   . "\u{1F605}"
	   . "\u{1F614}"
	   . "\u{1F644}";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>untitled</title>
<meta name="generator" content="Geany 1.34.1" />
</head>
<body>
<?= $emoji ?>
</body>
</html>
  • Running an OS command using "back ticks"
<?php
$path = 'C:\Users\ACER\Repos\classic_php_examples';
$cmd  = 'ls -l ' . $path;
if (PHP_OS_FAMILY === 'Windows') {
	$out  = `dir *.*`;
} else {
	$out  = `ls -l *`;
}
echo $out;
  • Using the "spread" operator (also called "splat" operator) to flatten two arrays
<?php
$a = [111, 222, 333];
$b = [444, 555, 666];
$c = [...$a, ...$b];
echo $c[5]; // would like to see "666"
var_dump($c);

// gives the same results as $c
$d = array_merge($a, $b);
echo $d[5]; // would like to see "666"
var_dump($d);
  • Example using the "spread" operator to "pack" arguments into an array
<?php
function sum_of_values($label, ...$a)
{
	return $label . ' ' . array_sum($a);
}

echo sum_of_values('The sum is', 1, 2, 3, 4, 5, 6);
echo "\n";
echo sum_of_values('The total is', 11, 22, 33);
  • Alternate array assignment example
<?php
// Build the crew
$mission = [
	'STS395' => [
		2176 => ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
		3294 => ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
		1122 => ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist']
	]
];

// Output all elements
echo $mission['STS395'][2176]['lastName'];
  • Searching for a value in a multi-dimensional array
// Build the crew
$mission = [
    'STS395' => [
        ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
        ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
        ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist'],
        // use this approach if pre-assigning values
        ['firstName' => 'Betty', 'lastName' => 'Rubble', 'specialty' => 'Cavewoman'],
    ]
];
// typical for a progammatic add someplace in your code at runtime
$mission['STS395'][] = ['firstName' => 'Fred', 'lastName' => 'Flintstone', 'specialty' => 'Caveman'];
echo "\n {$mission['STS395'][4]['firstName']}  {$mission['STS395'][4]['lastName']}\n"; // output: Fred Flintstone
var_dump($mission);
echo "\n" . __LINE__ . "\n";

// extract the last names from the multi-dim array:
$lastNames = array_column($mission['STS395'], 'lastName');
var_dump($lastNames);
// locate the key of the last name specified:
$key = array_search('Flintstone', $lastNames);
if (!empty($key)) echo implode(',',$mission['STS395'][$key]);
  • Nested foreach() loops
<?php
$missions = [
    'STS395' => [
        ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
        ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
        ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist']
    ]
];

foreach($missions as $mission => $astronauts) {
	echo 'Processing mission: ' . $mission . PHP_EOL;
	foreach($astronauts as $y => $row) {
		echo 'Crew Member: ' . ($y + 1) . PHP_EOL;
		foreach ($row as $x => $item) {
			echo $x . "\t" . $item . "\n";
		}
	}
}
  • Example from homework accepting URL parameter "day"
$allowed   = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
$dayOfWeek = $_GET['day'] ?? '';
$key = array_search($dayOfWeek, $allowed);
if ($key !== FALSE) {
	$dayOfWeek = $allowed[$key];
} else {
	$dayOfWeek = date('D');
}

if ($dayOfWeek === 'Fri') {
    echo 'See you on Monday';
} elseif ($dayOfWeek === 'Sat') {
    echo 'See you on Monday';
} else {
    echo 'See you tomorrow';
}

Example of for loop using return value of a function as a condition

function misc()
{
	return rand(0,99);
}

for ( $i = 1; $i < misc(); $i *= 2) {
    echo $i . ' ';
}

Example using continue: reads from a CSV file

<?php
$mission = [
	'STS395' => [
		2176 => ['Mark', 'Watney', 'Botanist'],
		3294 => ['Melissa', 'Lewis', 'Commander'],
		1122 => ['Beth', 'Johanssen', 'Computer Specialist']
	]
];

foreach ($mission['STS395'] as list($first, $last, $spec)) {
	echo "$first $last is a $spec\n";
}

$mission = [
	'STS395' => [
		2176 => ['firstName' => 'Mark', 'lastName' => 'Watney', 'specialty' => 'Botanist'],
		3294 => ['firstName' => 'Melissa', 'lastName' => 'Lewis', 'specialty' => 'Commander'],
		1122 => ['firstName' => 'Beth', 'lastName' => 'Johanssen', 'specialty' => 'Computer Specialist']
	]
];

foreach ($mission['STS395'] as list('firstName' => $first, 'lastName' => $last, 'specialty' => $spec)) {
	echo "$first $last is a $spec\n";
}

// conventional approach:
foreach ($mission['STS395'] as $val) {
	$first = $val['firstName'] ?? '';
	$last = $val['lastName'] ?? '';
	$spec = $val['specialty'] ?? '';
	echo "$first $last is a $spec\n";
}

  • Why you need to use type-hinting
<?php
function parse1($arr)
{
	$out = '';
	foreach ($arr as $key => $val)
		$out .= $key . ':' . $val . "\n";
	return $out;
}

// this works
$whatever = ['AAA' => 111, 'BBB' => 222, 'CCC' => 333];
echo parse1($whatever);

// Without type hinting, the error leads you to the wrong place
// The problem is not in the `foreach()` loop, the problem is on line 17!
// PHP Warning:  foreach() argument must be of type array|object, string given on line 5
echo parse1('ABC');

// use type hinting to protect vulnerable statements inside the function
// in this case it's the `foreach()` loop:
function parse2(iterable $arr)
{
	$out = '';
	foreach ($arr as $key => $val)
		$out .= $key . ':' . $val . "\n";
	return $out;
}

// With type hinting, the error points to the correct place
// Fatal error: Uncaught TypeError: parse2(): Argument #1 ($arr) must be of type iterable,
// string given, called in test.php on line 32 ...
echo parse2('ABC');
  • Example retrieving the first and last values of an array
    • NOTE: could also use array_key_first() and array_key_last() if running PHP 8+
<?php
$arr = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
echo $arr[0] . ' to ' . $arr[count($arr) - 1];
echo PHP_EOL;

reset($arr);
echo current($arr);
echo ' to ';
end($arr);
echo current($arr);
echo PHP_EOL;

// actual output:
/*
Mon to Sun
Mon to Sun
*/
  • Example using a lookup array for static list of state codes
<?php
$states = [
	'CA' => 'California',
	'NY' => 'New York',
	'RI' => 'Rhode Island',
	'MA' => 'Massachusetts',
	'NJ' => 'New Jersey',
];

$code = $_GET['state'] ?? '';
// validate the input
if (!isset($states[$code])) {
	error_log('Invalid state code input');
	exit('Not found');
}
echo $states[$code];
  • Example using named parameters to set a cookie with the httponly flag
<?php
setcookie('test', 111, httponly: TRUE);
if (session_status() === PHP_SESSION_ACTIVE) {
  // good to go!
}

Functions

Example using unlimited number of args

<?php
function sum(...$args)
{
	return array_sum($args);
}

echo sum(1,2,3,4,5,6,7,8,9);
echo PHP_EOL;
echo sum(111,222);
echo PHP_EOL;

Calling functions example:

<?php
function random() : int
{
	return rand(0,999);
}

function sum(int $a) : int
{
	// calling one function from inside another
	return $a + random();
}

echo sum(123);
echo PHP_EOL;
// the return from random() becomes the arg passed to sum()
echo sum(random());
echo PHP_EOL;

Indirection in function calling using $$

<?php

function processUser(...$args){
    $output = null;
    //$args = func_get_args();
	var_dump($args);
    if(count($args)){
        foreach($args as $arg){
            $output .= $arg . PHP_EOL;
        }
    }
    return $output;
}

$process = 'processUser';
$x = 'process';
// NOTE: has the same effect as this:
// echo processUser('Mark', 'Watney', 'Botanist', 'Whatever', 12345);
echo $$x('Mark', 'Watney', 'Botanist', 'Whatever', 12345);

Example of return by reference

  • Form data validation
<?php
function validate(array $inputs, array &$messages) : bool
{
	$ok = TRUE;
	if (!isset($inputs['name'])) {
		$messages[] = 'Missing name';
		$ok = FALSE;
	} else {
		if (!ctype_alpha($inputs['name'])) {
			$messages[] = 'Name must have only alpha characters';
			$ok = FALSE;
		}
	}
	return $ok;
}

$inputs = $_GET;
$messages = [];
if (validate($inputs, $messages)) {
	// OK to process
	echo 'OK';
} else {
	echo implode('<br />', $messages);
}

Anonymous functions examples:

<?php
function get_select(string $table, string $where, PDO $pdo)
{
	$sql = 'SELECT * FROM ' . $table . ' WHERE ' . $where;
	return function () use ($sql) {
		$stmt = $pdo->query($sql);
		$result = [];
		while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
			$result[] = $row;
		}
		return $result;
	};
}
$select = get_select('orders', 'id = 1', $pdo);

// do something
// ...
// do something

foreach ($select() as $row) {
	// display the row
	// maybe using printf()
}

I/O

Package that goes directly to Excel:

<?php
$path = __DIR__ . '/*.txt';
$search = 'printf';
$list = glob(__DIR__ . '/*.php');
foreach ($list as $fn) {
	$contents = file($fn);
	foreach ($contents as $num => $line) {
		if (strpos($line, $search) !== FALSE) {
			echo $fn . PHP_EOL;
			printf("%4d : %s\n", $num, $line);
		}
	}
}

Example from the "f" labs:

<?php
$path = __DIR__;
$filename = $path . '/Text3.txt';
// open in "append" mode
// add "+" if you need to later read the file as well
$handler = fopen($filename, 'a+');
if (!$handler) {
	echo 'Cannot open file.';
} else {
//  don't need to do this:
//	fseek($handler,SEEK_END);
	$data = 'Another day in paradise!';
	$text = $data .PHP_EOL ;
	$bytes = fwrite($handler,$text);
	echo $bytes . ' bytes written.';
	echo "File contents:\n";
	// moves file pointer to start
	rewind($handler);
	// echoes entire file
	fpassthru($handler);
	echo PHP_EOL;
	fclose($handler);
}

IMPORTANT: the entire file will be in memory at one point when using file_get_contents()

Another example:

<?php
$path = __DIR__;
$filename = $path . '/Text3.txt';
//$data = file_get_contents($filename);
$data = 'New line3 added.' . PHP_EOL;
file_put_contents( $filename, $data, FILE_APPEND);
//echo file_get_contents($filename);
readfile($filename);

If you need to increase the runtime memory allocation:

// IMPORTANT: use with extreme caution!
// default RAM allocation for a single PHP run == 128M
ini_set('memory_limit', '256M');	// increases run limit to 256 MB of RAM
ini_set('memory_limit', '1G');	// increases run limit to 1 GB of RAM

All directives: https://www.php.net/manual/en/ini.list.php

Example of file info using printf():

<?php
printf('%50s : %5s : %3s' . PHP_EOL, 'Filename', 'Size', 'Num');
$pattern = '%50s : %5d : %3d' . PHP_EOL;
foreach (glob("*.php") as $filename) {
	printf($pattern, $filename, filesize($filename), count(file($filename)));
}

Web Stuff

To output a PDF /path/to/file.pdf

header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="file.pdf");
readfile('/path/to/file.pdf');
exit;

Using stripos() to detect OS and browser

<?php
$agent = $_SERVER['HTTP_USER_AGENT'];
switch (TRUE) {
	case stripos($agent, 'linux') :
		$os = 'Linux system detected';
		break;
	case stripos($agent, 'windows') :
		$os = 'Windows system detected';
		break;
	default :
		$os = 'Unknown';
}
switch (TRUE) {
	case stripos($agent, 'chrome') :
		$browser = 'Chrome browser detected';
		break;
	case stripos($agent, 'firefox') :
		$browser = 'Firefox browser detected';
		break;
	case stripos($agent, 'safari') :
		$browser = 'Firefox browser detected';
		break;
	default :
		$browser = 'Unknown';
}
echo "We have detected the following:<br />\n";
echo $browser . "<br />\n";
echo $os . "<br />\n";

Example of receiving data via "GET"

<?php
// data is received from the browser via the URL
$first = $_GET['first'] ?? '';
$last  = $_GET['last'] ?? '';
?>
<form method="get">
First Name: <input type="text" name="first" value="<?= $first ?>"/>
<br />
Last Name: <input type="text" name="last"  value="<?= $last ?>"/>
<br />
<input type="submit" />
</form>
<?php phpinfo(INFO_VARIABLES); ?>

Example of receiving data via "POST"

<?php
// data is received from the browser in the *body* of the request
$first = $_POST['first'] ?? '';
$last  = $_POST['last'] ?? '';
?>
<form method="post">
First Name: <input type="text" name="first" value="<?= $first ?>"/>
<br />
Last Name: <input type="text" name="last"  value="<?= $last ?>"/>
<br />
<input type="submit" />
</form>
<?php phpinfo(INFO_VARIABLES); ?>

Same thing with added security

<?php
// received from the browser in the *body* of the request
// IMPORTANT: all inputs should also be filtered, validated and sanitized
$first = trim(strip_tags($_POST['first'] ?? ''));
$last  = trim(strip_tags($_POST['last'] ?? ''));
?>
<form method="post">
<!-- "escape" the output using htmlspecialchars() for security reasons -->
First Name: <input type="text" name="first" value="<?= htmlspecialchars($first); ?>"/>
<!-- example of actual output: &lt;script&gt;alert(&#039;hahaha&#039;);&lt;/script&gt; -->
<br />
Last Name: <input type="text" name="last"  value="<?= htmlspecialchars($last); ?>"/>
<br />
<input type="submit" />
</form>
<?php phpinfo(INFO_VARIABLES); ?>
  • Use parse_url() to breakdown a URL into its parts
<?php
$url = 'https://mars-express.com/path/to/whatever?id=124&mission=STS395';
$parsed = parse_url($url);
var_dump($parsed);
// output
/*
 * array(4) {
  ["scheme"]=>
  string(5) "https"
  ["host"]=>
  string(16) "mars-express.com"
  ["path"]=>
  string(17) "/path/to/whatever"
  ["query"]=>
  string(21) "id=124&mission=STS395"
}
*/
  • Also use urlencode() for any data added to the base URL
<?php
$url = 'https://mars-express.com/path/to/whatever?';
echo $url . urlencode('status=Is this going to work?');

To see what's coming into your PHP program from HTTP:

<?php
phpinfo(INFO_VARIABLES);

Various form styles

  • Mainly HTML with PHP mixed in
  • Includes example of validating the name field
<?php
$days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
$allowed = ['Mon','Tue','Wed','Thu','Fri'];
$error = 0;
$name = '';
$email = '';
$message = '';
$daySelect = '';
$dayCheck  = [];
if (!empty($_POST)) {
	// validate name
	$name = $_POST['name'] ?? '';
	if ($name) {
		if (strlen($name) > 16) {
			$message .= "Name must be 16 chars or less\n";
			$error++;
		}
		if (!ctype_alpha($name)) {
			$message .= "Name must have only letters\n";
			$error++;
		}
		// example of filtering
		$name = strip_tags($name);
	}
	// validate day_select
	$daySelect = $_POST['day_select'] ?? '';
	if (!in_array($daySelect, $allowed)) {
		$message .= "Day was not included in the set of allowed days\n";
		$error++;
	}
}
$message .= ($error === 0) ? "Form data is valid\n" : "Form data has errors\n";
?>
<form method="post">
Name: <input type="text" name="name" value="<?= htmlspecialchars($name) ?>" />
<br />Email: <input type="email" name="email" />
<br />Date: <input type="date" name="date" />
<br /><select name="day_select">
<?php foreach ($days as $day) echo '<option>' . $day . '</option>'; ?>
</select>
<br />
<?php
foreach ($days as $day) {
	echo '<input type="checkbox" name="day_check[]" value="' . $day . '" />' . $day . '&nbsp;';
}
?>
</select>
<br /><input type="submit" />
</form>
<?= nl2br($message); ?>
<?php phpinfo(INFO_VARIABLES); ?>

Example from file labs

<?php
// single directory
$path = __DIR__;
$list = glob($path . '/*');
echo '<table>';
echo '<tr><th>Name</th><th>Size in Bytes</th><th>Lines</th></tr>';
foreach ($list as $fn) {
	echo '<tr>';
	echo "<td>" . basename($fn) . "</td>";
	echo '<td>' . filesize($fn) . '</td>';
	$lines = count(file($fn)) - 1;
	echo "<td>$lines</td>";
	echo '</tr>';
}
echo "</table>\n";

Example of form processing

<?php
$msg = '';
if(!empty($_GET)) {
    $email = $_GET['email'] ?? '';
    $date  = $_GET['date']  ?? '';
    if(!empty($email) && !empty($date)) {
        $email = filter_var(strip_tags(trim($email)), FILTER_SANITIZE_EMAIL);
        $date  = strip_tags(trim($date));
        $msg = 'Data is validated and sanitized, handle it ...';
    } else {
        $msg = 'Invalid input';
    }
} else {
    // no form data has been posted
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>untitled</title>
<meta name="generator" content="Geany 1.36" />
</head>
<body>
<form method="get">
	Email: <input name="email" type="email" />
	<br />
	Date: <input name="date" type="date" />
	<br />
	<input type="submit"/>
</form>
<hr />
<?= $msg; ?>
<?php phpinfo(INFO_VARIABLES); ?>
</body>
</html>

Example of cookie usage:

CSS tutorial

<?php
// received from the browser in the *body* of the request
// IMPORTANT: all inputs should also be filtered, validated and sanitized
$first = trim(strip_tags($_POST['first'] ?? ''));
$last  = trim(strip_tags($_POST['last'] ?? ''));
?>
<form method="post" enctype="multipart/form-data">
<!-- "escape" the output using htmlspecialchars() for security reasons -->
First Name: <input type="text" name="first" value="<?= htmlspecialchars($first); ?>"/>
<!-- example of actual output: &lt;script&gt;alert(&#039;hahaha&#039;);&lt;/script&gt; -->
<br />
Last Name: <input type="text" name="last"  value="<?= htmlspecialchars($last); ?>"/>
<br />
File Upload: <input type="file" name="upload" />
<br />
<input type="submit" />
</form>
<?php phpinfo(INFO_VARIABLES); ?>

Cookies:

For HTML forms:

  • Use method="GET" if you want the user to bookmark the form posting (i.e. site search)
  • Use method="POST" if you have a large amount of data to be posted in the form
    • Mandatory if you do a file upload!

Database

Rankings: https://db-engines.com/en/ranking

mysql> select * from customers as c join orders as o on c.id = o.customer;
+----+-----------+-----------+----+------------+----------+--------+--------------------------+----------+
| id | firstname | lastname  | id | date       | status   | amount | description              | customer |
+----+-----------+-----------+----+------------+----------+--------+--------------------------+----------+
|  4 | Susan     | Chu       |  1 | 1355097600 | complete |    560 |                          |        4 |
|  3 | J         | Flores    |  2 | 1359062345 | invoiced |   9800 |                          |        3 |
|  2 | Janet     | Levitz    |  3 | 1357948800 | held     |    300 |                          |        2 |
|  3 | J         | Flores    |  4 | 1359500400 | open     |     34 | Paper                    |        3 |
|  1 | George    | Stevenson |  5 | 1359586800 | open     |   4570 | PHP development          |        1 |
|  5 | Thomas    | White     |  6 | 1359586800 | invoiced |   2000 | Laptop                   |        5 |
|  3 | J         | Flores    |  7 | 1360796400 | open     |    300 | A big box of chocolates. |        3 |
+----+-----------+-----------+----+------------+----------+--------+--------------------------+----------+
7 rows in set (0.00 sec)
  • mysqli examples
# Database Examples -- 28 Apr 2023
<?php
function connect()
{
    // init database creds
    $db = [
        'dsn' => '127.0.0.1',
        'username' => 'vagrant',
        'password' => 'vagrant',
        'database' => 'phpcourse'
    ];
    return mysqli_connect($db['dsn'], $db['username'], $db['password'], $db['database']);
}

function select()
{
    $query = "SELECT * FROM customers ORDER BY lastname";
    $results = [];
    // Set the query
    $result = mysqli_query(connect(), $query);
    while($row = mysqli_fetch_assoc($result)) {
        var_dump($row);
    }
}

function insert(string $firstname, string $lastname)
{
    $query = "INSERT INTO customers (firstname, lastname) VALUES ('$firstname','$lastname')";
    return mysqli_query(connect(), $query);
}

function update(string $field, int|string $data, string $where)
{
    $query = "UPDATE customers SET $field = '$data' WHERE $where";
    return mysqli_query(connect(), $query);
}

function delete(string $where)
{
    $query = "DELETE FROM customers WHERE $where";
    return mysqli_query(connect(), $query);
}


try {
    //var_dump(insert('Wilma', 'Flintstone'));
    //var_dump(update('firstname', 'J', 'id=3'));
    var_dump(delete('id=7'));
    select();
} catch (Throwable $t) {
    echo $t->getMessage();
}

Miscellaneous

Use var_export($data, TRUE) to return a string that can be placed into the error log

Q & A

ERRATA

x* http://localhost:8881/#/4/55 x * 'mose'??? x* http://localhost:8881/#/5/59 x * Please change form name to "days" instead of "gender"

  • General Note:
    • Code blocks are hard to read
    • Last section (Language Updates): already in the course