Last: http://localhost:8883/#/4/40 3.16.180.170
-
Q: Other examples of SPL classic data structures:
-
A:
SplDoublyLinkedList
-
A:
SplHeap
-
A:
SplFixedArray
-
Q: Find more examples of iterators
-
A: Uses
ArrayIterator
andLimitIterator
for pagination -
A: Uses
InfiniteIterator
to build large array to demonstrate PHP 8 "stable sort" -
Q: Example of login and authentication
-
A: https://github.com/dbierer/filecms-website
- Login logic: https://github.com/dbierer/filecms-website/blob/main/templates/super/login.phtml
- Ongoing authentication: https://github.com/dbierer/filecms-website/blob/main/src/processing.php
-
A: https://github.com/dbierer/filecms-core
- Ongoing authentication verification:
- https://github.com/dbierer/filecms-core/blob/main/src/Common/Security/Profile.php
-
Q: Other examples of authentication?
-
A: Good starting point:
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
- Clone this repo: https://github.com/dbierer/php-iii-demos.git
- Contains code you can use with later labs
- Lab: FFI
- Lab: New Extension
- Lab needs additional work
- If you follow the instructions here exactly, "test1()" works, but "test2()" does not
- Lab: Custom PHP
- Read this instructions here: https://github.com/php/php-src
- Lab: ZendPHP for AWS [optional]
- Lab: Docker Image Build
- In
startup.sh
be sure to change this line:
- In
#!/bin/bash
export VER=81
-
Lab: Commit the Image
-
Lab: Docker Compose Labs
- Have a look at the article on Orchestration: https://www.zend.com/blog/what-is-cloud-orchestration
- Make sure you change
compose.startup.php-fpm.sh
toexport VER=81
so that you're using PHP 8.1
-
CLI utility to reset JIT:
-
VM Setup (update/upgrade + phpMyAdmin)
- phpMyAdmin: use the directions below
-
JMeter Setup
-
Jenkins Setup
- 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!
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
- 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
checking for BZip2 in default path... not found
configure: error: Please reinstall the BZip2 distribution
- https://unix.stackexchange.com/questions/658758/php-build-error-please-reinstall-bzip2-distribution
sudo apt install -y libbz2-dev
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
- https://stackoverflow.com/questions/35879203/linux-php-7-configure-error-please-reinstall-readline-i-cannot-find-readline
sudo apt install -y libreadline-dev
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
- NOTE: this error actually comes from installing the
GD
extension - See: https://www.php.net/manual/en/image.installation.php
- As of PHP 7.4.0,
--with-png-dir
and--with-zlib-dir
have been removed.libpng
andzlib
are required.
- As of PHP 7.4.0,
- See: https://askubuntu.com/questions/508934/how-to-install-libpng-and-zlib
sudo apt install -y libpng-dev zlib1g-dev
configure: error: Package requirements (libzip >= 0.11 libzip != 1.3.1 libzip != 1.7.0) were not met:
No package 'libzip' found
- https://stackoverflow.com/questions/45775877/configure-error-please-reinstall-the-libzip-distribution
sudo apt install -y libzip-dev
Final Solution:
sudo apt install -y libbz2-dev libpng-dev zlib1g-dev libsodium-dev \
libreadline-dev libcurl4-openssl-dev libbz2-dev
Full DateTime::format()
codes:
- https://www.php.net/manual/en/datetime.format.php
Getting differences between dates, use
diff()
<?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;
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);
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
implementsArrayAccess
<?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>';
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] {
}
...
*/
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;
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();
}
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:
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
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
You can also use a php.ini
setting of on
to enable JIT:
opcache.jit=on
this is an alias fortracing
- 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
Steps taken to launch an instance:
- Login to AWS
- Went to AWS marketplace
- Searched for "ZendPHP"
- Selected "ZendPHP with Apache on Ubuntu 20.04 (BYOL)"
- Clicked on "Subscribe"
- From next menu clicked on "Continue to Configuration"
- Choose configuration (including region, which could affect what services are in the "Free Tier")
- Chose "US East (N. Virginia)"
- Clicked on "Continue to Launch"
- Choose Action
- Chose "Launch through EC2"
- From "Launch an Instance" menu
- Chose "t2.micro" (free tier eligible)
- Chose "Create New Key Pair"
- RSA
- PEM
- Copied downloaded
*.pem
file to~/.aws
- Clicked "Launch Instance"
- From the next screen, chose "View Instance Details"
- Wrote down IP address
a.b.c.d
- Wrote down IP address
- Read instructions on connecting to the instance
- Shelled into 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
Orchestration:
- https://www.zend.com/webinars/orchestrating-your-php-applications
- https://www.zend.com/blog/what-is-cloud-orchestration
- https://www.zend.com/blog/what-is-cloud-orchestration Example Dockerfile:
- https://github.com/dbierer/Learn-MongoDB-4.x/blob/master/docker/Dockerfile
Example
docker-compose.yml
- A 3 container orchestrated system that represents a 3 node MongoDB replica set
- https://github.com/dbierer/Learn-MongoDB-4.x/blob/master/chapters/13/docker-compose.yml Terraform templates
- https://developer.hashicorp.com/terraform/language/functions/templatefile
- Low level example: https://github.com/dbierer/strat_post
- Good article on async programming: https://www.zend.com/blog/using-swoole-and-mezzio
Configuration Management tools
- Ansible
- Puppet
- https://github.com/dbierer/php-iii-demos.git
- https://github.com/dbierer/php-iii-jul-2022.git
- https://github.com/dbierer/php-iii-mar-2021.git
- https://github.com/dbierer/php-class-notes/blob/master/php-iii-may-2021.md
-
Q: RE: Docker Compose: what's the difference/advantage of "ipam" vs. "overlay" for building networks?
-
A:
IPAM
is an old acronym that stands for "IP Address Management". It's not a protocol. You useipam
as a sub-key under your network service mainly to define static IP address information.- See: https://docs.docker.com/compose/compose-file/#ipam
- Also check out the "ipv4_address, ipv6_address" sub-heading under https://docs.docker.com/compose/compose-file/compose-file-v3/#networks
-
A:
overlay
is a Docker network driver that allows communication between containers. -
A: Also see this article about fixed IP addresses in Docker containers:
-
Q: Link ZendPHP Terraform templates?
-
A: See: https://www.zend.com/blog/what-is-cloud-orchestration
-
A: See: https://www.zend.com/downloads/zendphp-terraform-templates
-
Q: What's the syntax to switch between PHP versions in Ubuntu/Debian?
-
A: The utility for Debian/Ubuntu is
update-alternatives
-
A: Example using Ubuntu 20.04:
$ 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:
-
A: If you're using ZendPHP, you can use
zendphpctl
to switch versions -
Q: Example of where Interfaces are used as type hints instead of classes?
-
A: Have a look at the Laminas framework:
- Most interfaces have the word "Interface" in their name
- See: https://github.com/laminas/laminas-mvc/blob/master/src/Application.php
-
A: In the Laravel framework, interfaces are generally under the
Illuminate\Contracts
namespace- Most interfaces do not have "Interface" in their name
- See: https://github.com/laravel/framework/tree/9.x/src/Illuminate/Contracts/Auth
-
Q: Example of
SplObjectStorage
used as a service container -
A: See: https://github.com/dbierer/classic_php_examples/blob/master/oop/App/Service/Manager.php
-
Q: Why is
STDOUT
still producing output even withob_start()
? -
A: Still researching. Unable to duplicate the problem on my main computer.
-
Q: Suggested
configure
options for Custom PHP Lab -
A: See below
- http://localhost:8883/#/2/29
- Add "and Classes" to the title "Predefined Interfaces"
- http://localhost:8883/#/2/45
- In
__unserialize()
the property s/be$purchases
- In
- http://localhost:8883/#/4/7
- Line 3 missing semi-colon!
- http://localhost:8883/#/7/11
- Title s/be "InsertHandler"
- http://localhost:8883/#/8/7
- s/be "... the operation has failed" (not "filed")
- http://localhost:8883/#/8/18
- s/be "You can HAVE as many channels ... "