![](https://web-notes.wirehopper.com/media/users/mushrooms/profile_pictures/.evocache/bb.jpg/crop-64x64.jpg?mtime=1692585013)
PHP Abstract Base Class - Including validation
Mar 19th
An example of a base class.
Sample extension and call for instantiation:
<?php
class Course extends Base {
public function __construct($properties) {
$this->_properties = array(
'id'=>array(
'required' => false,
'default' => null,
'filter' => FILTER_VALIDATE_INT,
'options' => array('min_range' => 0, 'max_range' => PHP_INT_MAX)),
'name' => array(
'required' => true,
'default' => null,
'filter' => FILTER_VALIDATE_REGEXP,
'options' => array('regexp' => '/^[\w\,\.\'\-\(\)\$ ]{3,}$/')),
'isCore' => array(
'required' => true,
'default' => true,
'filter' => FILTER_VALIDATE_BOOLEAN));
return parent::__construct($properties);
}
}
<?php
Abstract Class Base {
protected $_properties = null;
protected $_data = array();
protected $_valid = false;
protected $_error_message = array();
function __construct($properties = null) {
if (is_array($properties)) {
foreach ($this->_properties as $k => $v) {
if (isset($properties[$k])) {
$p = $properties[$k];
if (is_string($p)) {
$p = trim($p);
if ($p === "") {
if (empty($p) && $v['required']) { // this should never happen
return null;
}
}
}
} else {
$p = $v['default'];
}
$this->_data[$k] = $p;
}
$filter = filter_var_array($this->_data,$this->_properties);
if ($filter === false || in_array(false,$filter,true)) {
foreach ($filter as $k => $v) {
if (($v === false) &&
($this->_properties[$k]['filter'] !== FILTER_VALIDATE_BOOLEAN)) {
// This is an important test, a boolean false,
// validated as such, doesn't mean the filter
// failed.
trigger_error('Invalid data');
}
}
}
}
return $this->_data;
}
function __get($property) {
return isset($this->_data[$property]) ? $this->_data[$property] : null;
}
function __set($property, $value) {
$this->_data[$property] = $value;
}
function toJson() {
return json_encode($this->_data);
}
function isValid($property = null) {
if ($property === null)
return $this->_valid || (count($this->_error_message) == 0);
else
return !isset($this->_error_message[$property]);
}
function error($property) {
if (isset($this->_error_message[$property])) {
return $this->_error_message[$property];
} else {
return '';
}
}
}
![](https://web-notes.wirehopper.com/media/users/mushrooms/profile_pictures/.evocache/bb.jpg/crop-64x64.jpg?mtime=1692585013)
PHP filter_var_array Example
Mar 14th
This is a very simple PHP contact form validation script.
It fails silently, based on the expectation the client is validating the data prior to submitting it. In this case, if the server receives invalid inputs, inputs with invalid data, or a data set missing required inputs, it is assumed the data is either not being submitted by the expected client code, or it has been tampered with en route.
The silent fail is a die with no output. This provides no information for potentially malicious visitors.
If the data is valid, it is echoed back to the server JSON encoded.
if (isset($_POST) && !empty($_POST)) {
$required = array('name', 'email', 'interest', 'relationship', 'message');
$optional = array('phone', 'subscribe');
$inputs = array_merge($required, $optional);
foreach ($_POST as $k => $v) {
if (!in_array($k, $inputs)) {
die;
}
$v = trim($v);
if (!empty($v)) {
$data[$k] = $v;
} else {
if (in_array($k, $required)) {
die;
} else {
$data[$k] = '';
}
}
}
$filter = filter_var_array($data, array(
'name' => array('filter' => FILTER_VALIDATE_REGEXP,
'options' => array('regexp' => '/^[ \w\,\.\'\-]{5,}$/')),
'email' => FILTER_VALIDATE_EMAIL,
'interest' => array('filter' => FILTER_VALIDATE_REGEXP,
'options' => array('regexp' => '/^(Sales|Support)$/')),
'relationship' => array('filter' => FILTER_VALIDATE_REGEXP,
'options' => array('regexp' => '/^(Client|Partner|Vendor)$/')),
'subscribe' => array('filter' => FILTER_VALIDATE_REGEXP,
'options' => array('regexp' => '/^(on)?$/')),
'message' => array('filter' => FILTER_VALIDATE_REGEXP,
'options' => array('regexp' => '/^[\w\,\.\'\-\(\)\$ ]{5,}$/')),
'phone' => array('filter' => FILTER_VALIDATE_REGEXP,
'options' => array('regexp' => '/^(1[ \-\.])?(\d{3})?[ \-\.]?\d{3}[ \-\.]?\d{4}$/'))));
if ($filter === false || in_array(false,$filter)) {
die;
}
echo json_encode($filter);
}
In an environment where security is important (security is important in all environments), this code should be extended to include a unique token validation, where a token is sent to the client on the initial request and the next request must have the identical token or it will be considered invalid.
![](https://web-notes.wirehopper.com/media/users/mushrooms/profile_pictures/.evocache/bb.jpg/crop-64x64.jpg?mtime=1692585013)
PHP - ImageMagick command line Pie Chart
Feb 11th
This code was converted from a bash version to create pie charts. It is much faster and does not require bc on the server.
PHP is used to create the ImageMagick command line.
This code creates pie chart and legend images which can then be placed on a web page or added in to a document.
To call the code, create an array of values, instantiate the GFX objects, establish the label_color_map as an associative array where the label text is the key and the value is a hex color code, and finally create the chart, sending a name and the data array, like so:
$data = array(50, 70, 100, 3, 49);
$chart = new GFX();
$chart->label_color_map(array('one' => '#1c28a1',
'two' => '#600087',
'three' => '#107a3f',
'four' => '#bf0000',
'five' => '#ff6d00'));
$chart->piechart('project',$data);
<?php
Class GFX extends Base {
public function __construct($parms = null) {
$this->_properties = array('centerx','centery','width','height',
'radius');
if (!is_array($parms)) {
$parms['width'] = $parms ['height'] = 330;
$parms['centerx'] = $parms['centery'] = 160;
$parms['radius'] = 135;
}
parent::__construct($parms);
}
public function label_color_map ($map = array()) {
$this->_data['labels'] = array();
foreach ($map as $k => $v) {
$this->_data['labels'][$k] = $v;
}
}
public function piechart($name = null, $data = array()) {
// Thanks to: http://jbkflex.wordpress.com/2011/07/28/creating-a-svg-pie-chart-html5/
if ($name === null) return;
$total = array_sum($data);
$max=0;$kmax='None';
foreach ($data as $k => $v) {
$value[$k] = (int)(($v/$total) * 100);
if ($value[$k] > $max) {
$kmax=$k;
$max=$value[$k];
}
}
while (array_sum($value) < 100) {
$value[$kmax]++;
}
foreach ($data as $k => $v) {
$value[$k] = (int)(($v/$total) * 100);
if ($value[$k] > $max) {
$kmax=$k;
$max=$value[$k];
}
}
while (array_sum($value) < 100) {
$value[$kmax]++;
}
$centerx = $this->_data['centerx'];
$centery = $this->_data['centery'];
$radius= $this->_data['radius'];
$count=0;
$startAngle=0;
$endAngle=0;
$arc=0;
$x1=0;
$x2=0;
$y1=0;
$y2=0;
$pi=pi();
$cmd='convert -size '.$this->_data['width'].'x'.$this->_data['height'].' xc:white -stroke white -strokewidth 5 ';
foreach ($value as $k => $v) {
$startAngle=$endAngle;
$endAngle=$startAngle+(360*$v/100);
$theta = $pi*$startAngle/180;
$x1=$centerx+$radius*cos($theta);
$y1=$centery+$radius*sin($theta);
$theta = $pi*$endAngle/180;
$x2=$centerx+$radius*cos($theta);
$y2=$centery+$radius*sin($theta);
$arc = ($v >= 50) ? '1' : '0';
$cmd.=' -fill "'.$this->_data['labels'][$k].'" -draw "path \'M '.$centerx.','.$centery.' L '.$x1.','.$y1.
' A '.$radius.','.$radius.' 0 '.$arc.',1 '.$x2.','.$y2.' Z"';
$count++;
}
$cmd.=' '.escapeshellarg(_GFX_DIR_.'/'.$name.'_chart.jpg');
`$cmd`;
if (!is_array($this->_data['labels'])) return;
$KEY_SIZE=20;
$MARGIN=5;
$TEXT_X=$KEY_SIZE+$MARGIN;
$height = $TEXT_X * count($value);
$cmd = 'convert -size 135x'.$height.' xc:white -fill white ';
$label = " -font 'Nimbus-Sans-Bold' -stroke none -pointsize 12 ";
$count=0;
$y1=5;
foreach ($value as $k => $v) {
$y2=$y1+$KEY_SIZE;
$y3=$y2-$MARGIN;
$label.=' -fill "'.$this->_data['labels'][$k].'" -draw "rectangle 0,'.$y1.' '.$KEY_SIZE.','.$y2.'"'.
' -draw "text '.$TEXT_X.','.$y3.' '.escapeshellarg(/*$data[$k].' '.*/$v.'% '.$k).'"';
$count++;
$y1=$y1+$KEY_SIZE+$MARGIN;
}
$cmd.= $label.' '.escapeshellarg(_GFX_DIR_.'/'.$name.'_legend.jpg');
`$cmd`;
}
}
<?php
Abstract Class Base {
protected $_properties;
protected $_data;
protected $_valid = false;
protected $_error_message = array();
function __construct($props) {
$this->_data = array_fill_keys($this->_properties, null);
$this->_data = array_merge($this->_data, $props);
}
function __get($property) {
return isset($this->_data[$property]) ? $this->_data[$property] : null;
}
function __set($property, $value) {
$this->_data[$property] = $value;
}
function isValid($property = null) {
if ($property === null)
return $this->_valid || (count($this->_error_message) == 0);
else
return !isset($this->_error_message[$property]);
}
function error($property) {
if (isset($this->_error_message[$property])) {
return $this->_error_message[$property];
} else {
return '';
}
}
}
Many thanks to the link above for the chart algorithm.
This post courtesy of Worktrainer.
![](https://web-notes.wirehopper.com/media/users/mushrooms/profile_pictures/.evocache/bb.jpg/crop-64x64.jpg?mtime=1692585013)
bash - ImageMagick - Pie Chart Script
Feb 7th
This is a very simple script that will create a pie chart and legend using bash, bc, and ImageMagick. Many thanks to the link above which provided the algorithms to complete the SVG paths.
It accepts a list of parameters which must add up to 100. The script has two arrays, LABELS and COLORS which are applied to each parameter in order.
#!/bin/bash
function usage {
echo "Usage: `basename $0` piece1 piece2 piece3 ..."
echo -e "\tWhere each piece is a percentage of the pie"
echo -e "\tThe pieces must add up to 100"
exit $E_BADARGS
}
LABELS=("None" "Accept" "Defer" "Discard")
COLORS=("#1c28a1" "#107a3f" "#ff6d00" "#bf0000");
TARGET_DIR='images'
if [ $# -lt 3 ];
then
usage
fi;
arc=()
sum=0
for piece in "$@"
do
sum=$(( $piece + $sum ))
done
if [ $sum -ne 100 ];
then
usage
fi
WIDTHxHEIGHT='330x330'
RADIUS=135
CENTERX=160
CENTERY=160
count=0
startAngle=0
endAngle=0
arc=0
total=0
x1=0
x2=0
y1=0
y2=0
pi=$(echo "scale=10; 4*a(1)" | bc -l)
cmd='convert -size '$WIDTHxHEIGHT' xc:white -stroke white -strokewidth 5 '
first=0
for piece in "$@"
do
startAngle=$endAngle
endAngle=$(echo "scale=10;$startAngle+(360*$piece/100)" | bc -l)
x1=$(echo "scale=10;$CENTERX+$RADIUS*c($pi*$startAngle/180)" | bc -l)
y1=$(echo "scale=10;$CENTERY+$RADIUS*s($pi*$startAngle/180)" | bc -l)
x2=$(echo "scale=10;$CENTERX+$RADIUS*c($pi*$endAngle/180)" | bc -l)
y2=$(echo "scale=10;$CENTERY+$RADIUS*s($pi*$endAngle/180)" | bc -l)
if [ $piece -ge 50 ]
then
FIFTY=1
else
FIFTY=0
fi
cmd=$cmd"-fill '${COLORS[count]}' -draw \"path 'M $CENTERX,$CENTERY L $x1,$y1 A $RADIUS,$RADIUS 0 $FIFTY,1 $x2,$y2 Z'\" "
count=$(( $count + 1 ))
done
cmd=$cmd" $TARGET_DIR/idea_chart.jpg"
eval $cmd
KEY_SIZE=20
MARGIN=5
TEXT_X=$(( $KEY_SIZE+$MARGIN ))
legends=$(( $#*($KEY_SIZE+$MARGIN) ))
cmd='convert -size 125x'$legends' xc:white -fill white '
label=" -font 'Nimbus-Sans-Bold' -stroke none -pointsize 12 "
count=0;
y1=5
for piece in "$@"
do
y2=$(( $y1+$KEY_SIZE ))
y3=$(( $y2-$MARGIN ))
label=$label"-fill '${COLORS[count]}' -draw 'rectangle 0,$y1 $KEY_SIZE,$y2 ' -draw \"text $TEXT_X,$y3 '$piece% ${LABELS[count]}'\" "
count=$(( $count + 1 ))
y1=$(( $y1+$KEY_SIZE+$MARGIN ))
done
cmd=$cmd$label" $TARGET_DIR/idea_legend.jpg"
eval $cmd
exit;
The FIFTY is used when an arc will span 50% or more of the pie, it sets the large-arc-flag.
This post courtesy of Worktrainer.
![](https://web-notes.wirehopper.com/media/users/mushrooms/profile_pictures/.evocache/bb.jpg/crop-64x64.jpg?mtime=1692585013)
dgrid Print Shim
Feb 2nd
If you need to print a dgrid, you can query the store and create an HTML table with JavaScript.
Create and work with the dgrid as usual, but add a second div below it which will contain the table.
<div class="no-print" id="grid"></div>
<div class="print-only" id="grid_print"></div>
Then set up the CSS to hide the print-only by default. Create a print.css file and the appropriate media attribute hide the no-print class and display the print-only.
Relevant lines of print.css
.print-only {
display: block;
}
.print-only table {
width: 100%;
table-layout: fixed;
}
.print-only table th {
border: 1px solid #000;
font-weight: bolder;
}
Relevant lines of admin.css (screen)
.print-only,.page-break {
display:none;
}
Link tag for the print.css file
<link media="print” href=’/css/print.css’ rel=’stylesheet’ type=’text/css’>
JavaScript to create the table. This code is used on a page with several grids, the grids are in the grids object, with stores in the stores object.
byId("print_button").onclick = function() {
/* This will find all the print_grids */
query("[id$='_grid_print']").forEach(function(node) {
if (node.hasChildNodes()) {
node.removeChild(node.firstChild);
}
var grid, grid_id;
grid_id = node.id.replace(/_grid_print/,'');
grid = grids[grid_id];
var store = stores[grid_id];
var data = store.query({});
if (data.length === 0) {
var no_data = document.createTextNode("None at this time");
node.appendChild(no_data);
} else {
var table, tr, th, td, content, c;
var hasNumber = false;
table = document.createElement("table");
tr = document.createElement("tr");
for (c in grid.columns) {
content = document.createTextNode(grid.columns[c].label);
th = document.createElement("th");
th.appendChild(content);
tr.appendChild(th);
if (c === "number") hasNumber = true;
}
table.appendChild(tr);
if (hasNumber) {
data = store.query({},[{attribute:"number"}]);
}
var i = 1,j,l;
l = data.length;
for (j=0; j<l; j++) {
tr = document.createElement("tr");
for (c in grid.columns) {
content = document.createTextNode(data[j][c]);
td = document.createElement("td");
td.appendChild(content);
tr.appendChild(td);
}
table.appendChild(tr);
i++;
}
node.appendChild(table);
}
});
window.print()
};
The hasNumber code is used to order the rows by a number in one of the columns.
This post courtesy of Worktrainer.