Skip to content

Latest commit

 

History

History
785 lines (703 loc) · 22.6 KB

php-iii-dec-2022.md

File metadata and controls

785 lines (703 loc) · 22.6 KB

PHP III - Dec 2022

Last: http://localhost:8883/#/4/40 3.16.180.170

TODO

Homework

For Thu 16 Dec 2022

  • Lab: REST Service Development
  • Lab: Adding Middleware
    • Take the code from the slides
    • Add a middleware request handler that implements an update (HTTP "PATCH")

For Tue 14 Dec 2022

#!/bin/bash
export VER=81

VM Update

  • After the VM first comes up, if you're not prompted to update, reboot the VM
  • After reboot: Select yes to "Update System Software" if you're prompted
  • Open a terminal window
  • Upgrade everything:
    • DO NOT update to Ubuntu 22.04!
sudo apt -y update
sudo apt -y upgrade
  • If asked to retain the database configuration select "OK" NOTE: this could take some time!

Install phpMyAdmin

Download the latest version from https://www.phpmyadmin.net Make note of the version number (e.g. 5.2.0)

  • From a terminal window:
cd /tmp
set VER=5.2.0
mv Downloads/phpMyAdmin-$VER-all-languages.zip .
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

Custom PHP Lab Notes

  • Clone from github
  • Switch to branch php-8.2.0
git checkout php-8.2.0
  • Follow the instructions
  • Be sure to install the pre-requisites!
  • Suggested ./configure options (place this all on one line):
./configure  \
    --enable-cli \
    --enable-filter \
    --with-openssl \
    --with-zlib \
    --with-curl \
    --enable-pdo \
    --with-libxml \
    --with-iconv \
    --enable-cgi \
    --enable-session \
    --with-pdo-mysql \
    --enable-phar \
    --with-pdo-sqlite \
    --with-pcre-jit \
    --with-zip \
    --enable-ctype \
    --enable-gd \
    --enable-bcmath \
    --enable-sockets \
    --with-bz2 \
    --enable-exif \
    --enable-intl \
    --with-gettext \
    --enable-opcache \
    --enable-fileinfo \
    --with-readline \
    --with-sodium

Dependency errors:

checking for BZip2 in default path... not found
configure: error: Please reinstall the BZip2 distribution
configure: error: Package requirements (libcurl >= 7.29.0) were not met:
No package 'libcurl' found
  • sudo apt install -y libcurl4-openssl-dev
configure: error: Please reinstall readline - I cannot find readline.h
configure: error: Package requirements (libsodium >= 1.0.8) were not met:
No package 'libsodium' found
  • sudo apt install -y libsodium-dev
configure: error: Package requirements (zlib) were not met:
No package 'zlib' found
configure: error: Package requirements (libzip >= 0.11 libzip != 1.3.1 libzip != 1.7.0) were not met:
No package 'libzip' found

Final Solution:

sudo apt install -y libbz2-dev  libpng-dev zlib1g-dev libsodium-dev \
                    libreadline-dev libcurl4-openssl-dev libbz2-dev

Advanced PHP

Full DateTime::format() codes:

<?php
$date1 = new DateTime('2022-11-11');
$date2 = new DateTime('2022-11-29');
$diff  = $date1->diff($date2);
var_dump($diff);
echo $diff->days . ':' . $diff->invert;
echo PHP_EOL;


$diff  = $date2->diff($date1);
var_dump($diff);
echo $diff->days . ':' . $diff->invert;
echo PHP_EOL;
// $invert property tell you if it's in the past or future
// see: https://www.php.net/manual/en/class.dateinterval.php

Adding a date, create a DateInterval instance

<?php
$date = new DateTime('now');
$date->add(new DateInterval('P92D'));
echo $date->format('l, j M Y');
// example output: Wednesday, 3 Mar 2023

Relative time formats

<?php
$date = new DateTime('third thursday of next month');
echo $date->format('l, j M Y');
echo PHP_EOL;

$date = new DateTime('last day of last month');
echo $date->format('l, j M Y');
echo PHP_EOL;

Anonymous Class

Example where the return value is an anon class with different methods to render its data

<?php
class Test
{
    public function getObject(array $arr)
    {
		return new class ($arr) {
			public $arr = [];
			public function __construct(array $arr)
			{
				$this->arr = $arr;
			}
			public function asHtml()
			{
				$html = '<ul>' . PHP_EOL;
				foreach ($this->arr as $item) $html .= '<li>' . $item . '</li>' . PHP_EOL;
				$html .= '</ul>' . PHP_EOL;
				return $html;
			}
			public function asJson()
			{
				return json_encode($this->arr, JSON_PRETTY_PRINT);
			}
		};
	}
}

$arr = ['AAA','BBB','CCC','DDD'];
$obj = (new Test())->getObject($arr);
echo $obj->asHtml();
echo $obj->asJson();
var_dump($obj->arr);
var_dump($obj);

Interfaces

Traversable

Traversable connects the old approach (Iterator) with a newer approach (IteratorAggregate)

<?php
class Test implements IteratorAggregate
{
        protected $name = 'Doug';
        protected $country = 'Thailand';
        protected $language = 'EN';
        public function getIterator()
        {
                return new ArrayIterator(get_object_vars($this));
        }
}

$test = new Test();
foreach($test as $key => $value) echo $key . ':' . $value . PHP_EOL;

Yet another example:

<?php
class User implements IteratorAggregate
{
	public $first = 'Fred';
	public $last  = 'Flintstone';
	public $role  = 'Caveman';
	public $date  = NULL;
	public function __construct()
	{
		$this->date = new DateTime();
	}
	public function getIterator()
	{
		$list = get_object_vars($this);
		$list['date'] = $this->date->format('l, j M Y');
        return new ArrayIterator($list);
    }
}

$user = new User();

function looper(Traversable $trav)
{
	foreach ($trav as $key => $val) echo "$key\t$val\n";
}

looper($user);

In this example, note that if we uncomment line 8, the legacy code still works

  • The reason is because ArrayObject implements ArrayAccess
<?php
$arr = [
	'first' => 'Fred',
	'last'  => 'Flintstone',
	'amount' => 99.99,
];
// if you uncomment the next line, $arr becomes an object, but the remaining code works OK
// $arr = new ArrayObject($arr);

// some other code

$purch = $_GET['purch'] ?? 1.11;
$arr['amount'] += $purch;

// some other code

// final output:
echo '<table>';
foreach ($arr as $key => $val)
	echo '<tr><th>' . $key . '</th><td>' . $val . '</td></tr>' . PHP_EOL;
echo '</table>';

Stringable (new in PHP 8)

Anytime you implement __toString()

<?php
class Test
{
        protected $name = 'Doug';
        protected $country = 'Thailand';
        protected $language = 'EN';
        public function __toString()
        {
                return var_export(get_object_vars($this), TRUE);
        }
}

$test = new Test();
echo $test;
echo PHP_EOL;
$reflect = new ReflectionObject($test);
echo $reflect;
echo PHP_EOL;

// output
/*
 * Object of class [ <user> class Test implements Stringable ] {
  @@ C:\Users\azure\Desktop\test.php 2-11

  - Constants [0] {
  }
  ...
*/

ArrayAccess Interface

It's treated just like an array

<?php
$user = [
        'user' => 'joe',
        'email'  => '[email protected]',
        'address' => '123 Main Street',
        'city' => 'Utrecht',
        'country' => 'NL',
];

$user = new ArrayObject($user);
$user['status'] = 'OK';

echo 'Name  :' . $user['user'] . PHP_EOL;
echo 'Email :' . $user['email'] . PHP_EOL;
echo 'City  :' . $user['city'] . PHP_EOL;
echo 'Status:' . $user['status'] . PHP_EOL;

Iterators

ArrayIterator example

<?php
$data = [
        'F' => 666,
        'A' => 111,
        'E' => 555,
        'C' => 333,
        'B' => 222,
        'D' => 444,
];

// here's the traditional way to use a while() with an array:
asort($data);
$pos   = 0;
$count = count($data);
while ($pos++ < $count) {
        echo key($data) . ':' . current($data) . PHP_EOL;
        next($data);
}

// same thing but using ArrayIterator:
$it = new ArrayIterator($data);
$it->asort();
while ($it->valid()) {
        echo $it->key() . ':' . $it->current() . PHP_EOL;
        $it->next();
}

SPL

Example of linked list:

<?php
$base = [
	'A' => 111,
	'B' => 222,
	'C' => 333,
	'D' => 444,
	'E' => 555,
	'F' => 666,
];

$link = ['F','E','D','C','B','A'];

foreach ($link as $key)
	echo $base[$key] . PHP_EOL;

Example of doubly linked list (using just arrays)

<?php
$base = [
	'A' => 111,
	'B' => 222,
	'C' => 333,
	'D' => 444,
	'E' => 555,
	'F' => 666,
];

$reverse = ['F','E','D','C','B','A'];
$forward = ['A','B','C','D','E','F'];

function showBase(array $link, array $base)
{
	foreach ($link as $key)
		echo $base[$key] . PHP_EOL;
}

echo showBase($forward, $base);
echo showBase($reverse, $base);

Example of doubly linked list (using SplDoublyLinkedList)

<?php
$obj = new SplDoublyLinkedList();
$obj[] = 111;
$obj[] = 222;
$obj[] = 333;
$obj[] = 444;
$obj[] = 555;
$obj[] = 666;

function showBaseObj(object $obj)
{
	foreach ($obj as $value)
		echo "$value\n";
}

echo showBaseObj($obj);
$obj->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO );
echo showBaseObj($obj);

SplSubject and SplObserver used to form a pipeline:

CLI

Example using both CLI args and interactive:

<?php
$usage = "Usage: php test.php -s | -i \n";
$param = $_SERVER['argv'][1] ?? '-i';
if ($param === '-s') var_dump($argv);
$cmd = readline('What do you want to do? ');
echo $cmd . PHP_EOL;
  • Also: notice that Composer has an extensive CLI capability
$ php composer.phar require
Search for a package: phpunit
Found 15 packages matching phpunit

   [0] phpunit/phpunit
   [1] phpunit/php-timer
   [2] phpunit/php-text-template
   [3] phpunit/php-file-iterator
   [4] phpunit/php-code-coverage
   [5] phpunit/phpunit-mock-objects Abandoned. No replacement was suggested.
   [6] symfony/phpunit-bridge
   [7] jean85/pretty-package-versions
   [8] phpunit/php-invoker
   [9] phpunit/php-token-stream Abandoned. No replacement was suggested.
  [10] johnkary/phpunit-speedtrap
  [11] phpstan/phpstan-phpunit
  [12] brianium/paratest
  [13] yoast/phpunit-polyfills
  [14] spatie/phpunit-snapshot-assertions
  • If you're using OOP, consider using Symfony\Console

Stream Wrapper Example

runStreamDb.php:

<?php
/**
 * StreamDb Runner
 */

require __DIR__ . '/../../../vendor/autoload.php';
use src\ModAdvancedTechniques\IO\StreamDb;

stream_wrapper_register('myDb', StreamDb::class);

// Stream write to a row
$user = 'vagrant';
$pwd  = 'vagrant';
$host = '127.0.0.1';
$uri  = 'myDb://' . $user . ':' . $pwd . '@' . $host . '/php3/1';
$resource = fopen($uri, 'w');
if($bytesAdded = fwrite($resource, 'TEST: ' . date('Y-m-d H:i:s'))) echo $bytesAdded . ' bytes Written';
fclose($resource);

// Stream read from a table row.
$resource = fopen($uri, 'r');
var_dump(fread($resource, 4096));

StreamDb.php:

<?php
/**
 * Custom Stream Wrapper and Runner
 */
namespace src\ModAdvancedTechniques\IO;
class StreamDb {
    const TABLE = 'data';
    const SQL_SELECT = 'SELECT * FROM `%s` WHERE id=%d';
    const SQL_UPDATE = 'UPDATE `%s` SET data=:data WHERE id=:id';
    const SQL_INSERT = 'INSERT INTO `%s` (id, data) VALUES (:id, :data)';

    protected $stmt, $position, $data, $url, $id, $mode;

    public function stream_open($url, $mode)
    {
        $result = FALSE;
        $this->position = 0;
        $url = parse_url($url);
        $path = explode('/', $url['path']);
        $this->id = (int) $path[2];
        if (empty($this->id)) $this->id = 1;
        $this->mod = $mode ?? 'r';
        try{
            $pdo = new \PDO("mysql:host={$url['host']};dbname={$path[1]}",
                $url['user'], $url['pass'], [\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION]);
        } catch(\PDOException $e){return $result;}

                switch ($mode) {
                        case 'w' :
                                $pdo->exec('DELETE FROM ' . static::TABLE . ' WHERE id=' . $this->id);
                                $this->stmt = $pdo->prepare(sprintf(static::SQL_INSERT, static::TABLE));
                                break;
                        case 'a' :
                                $this->stmt = $pdo->prepare(sprintf(static::SQL_UPDATE, static::TABLE, $this->id));
                        case 'r' :
                        default :
                                $this->stmt = $pdo->prepare(sprintf(static::SQL_SELECT, static::TABLE, $this->id));
                }
        return TRUE;
    }

    public function stream_write($data)
    {
        $strlen = strlen($data);
        $this->position += $strlen;
        $binding = ['id' => $this->id, 'data' => $data];
        //echo __METHOD__ . ':' . var_export($binding, TRUE) . ':' . var_export($this->stmt, TRUE); exit;
        return $this->stmt->execute($binding) ? $strlen : null;
    }

    public function stream_read()
    {
        $this->stmt->execute();
        if($this->stmt->rowCount() == 0) return false;
        return implode(',', $this->stmt->fetch());
    }

    public function stream_tell()
    {
        return $this->id;
    }

    function stream_eof()
    {
        return (bool) $this->stmt->rowCount();
    }
}

SQL to create table in the php3 database in the VM:

CREATE TABLE `data` (
  `id` int unsigned NOT NULL,
  `data` varchar(255) NOT NULL,
  `mod_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

JIT

You can also use a php.ini setting of on to enable JIT:

  • opcache.jit=on this is an alias for tracing
  • Also, don't forget to enable opcache itself
  • In addition: set a memory size for JIT (otherwise it won't work)
; example:
opcache.jit_buffer_size=32M

ZendPHP on AWS

Steps taken to launch an instance:

ssh -i .aws/php_iii_dec_2022.pem [email protected]
  • Set up sample app in /var/www/html
cd /var/www
sudo wget https://opensource.unlikelysource.com/post_code_test_app.zip
sudo apt install unzip
sudo unzip post_code_test_app.zip
sudo rm html/index.html
  • Tested from browser:
http://a.b.c.d/city=levittown&state=NY

Docker

Orchestration:

Middleware

Async

CI/CD

Configuration Management tools

  • Ansible
  • Puppet

Resources

Q & A

$ sudo update-alternatives --config php
There are 2 choices for the alternative php (providing /usr/bin/php).

  Selection    Path                  Priority   Status
------------------------------------------------------------
  0            /usr/bin/php8.1-zend   81        auto mode
  1            /usr/bin/php7.4-zend   74        manual mode
* 2            /usr/bin/php8.1-zend   81        manual mode

Press <enter> to keep the current choice[*], or type selection number:

ERRATA