# Wizardry and Steamworks JavaScript Libraries

was.php was.php General Purpose Library

# Latitude Longitude and Mercator Coordinates

The following snippets, adapted from Greg Schechter's MSN Blog can be used to convert between Latitude and Longitude and mercator coordinates. This can be used with a mercator projection map such as the following map:

## Latitude Longitude to Pixel

The function wasGeoToMercatorPoint takes as input:

• latitude
• longitude
• map width
• map height
• left-most map (0 if using the whole map of the world)
• right-most map (0 if using the whole map of the world)

and returns an array containing the map coordinate.

function wasGeoToMercatorPoint($latitude,$longitude, $mapWidth,$mapHeight, $mapLeft=0,$mapTop=0) {
$x =$mapLeft + ($longitude + 180) *$mapWidth / 360;
$y =$mapTop + ($mapHeight/2) * (1-(log(tan(deg2rad($latitude)/2 + M_PI/4))) / M_PI);
return array($x,$y);
}

## Pixel to Latitude and Longitude

The function wasGeoToMercatorPoint takes as input:

• x - pixel location
• y - pixel location
• map width
• map height
• left-most map (0 if using the whole map of the world)
• right-most map (0 if using the whole map of the world)

and returns the latitude and longitude corresponding to the pixel on the map.

function wasMercatorPointToGeo($x,$y, $mapWidth,$mapHeight, $mapLeft=0,$mapTop=0) {
$f = (($x-$mapLeft) * 360 /$mapWidth) - 180;
$l = rad2deg (2 * atan ( exp ( M_PI * (1 - 2 * ($y-$mapTop)/$mapHeight ) ) ) - M_PI/2);
return array($l,$f);
}

# Get Client IP

The variable:

$_SERVER['REMOTE_ADDR'] contains the address that the client connects from. The variable:$_SERVER['HTTP_X_FORWARDED_FOR']

contains the IP, if the client is using a proxy server.

The variable:

$_SERVER['SERVER_ADDR'] contains the IP address that the server is listening on. # Remove Empty Strings From Array Filter the array through the built-in strlen function:$clean_array = array_filter($dirty_array, 'strlen'); # Re-Index Array$clean_array = array_values($dirty_array); # JSON like Javascript Wrapper #colin.mollenhour.com function json_decode_nice($json, $assoc = FALSE){$json = str_replace(array("\n","\r"),"",$json);$json = preg_replace('/([{,]+)(\s*)([^"]+?)\s*:/','$1"$3":',$json); return json_decode($json,$assoc); } # Create UUID <?php class UUID { /** * Generates version 1: MAC address */ public static function v1() { if (!function_exists('uuid_create')) return false; uuid_create(&$context);
uuid_make($context, UUID_MAKE_V1); uuid_export($context, UUID_FMT_STR, &$uuid); return trim($uuid);
}

/**
* Generates version 3 UUID: MD5 hash of URL
*/
public static function v3($i_url) { if (!function_exists('uuid_create')) return false; if (!strlen($i_url))
$i_url = self::v1(); uuid_create(&$context);
uuid_create(&$namespace); uuid_make($context, UUID_MAKE_V3, $namespace,$i_url);
uuid_export($context, UUID_FMT_STR, &$uuid);
return trim($uuid); } /** * Generates version 4 UUID: random */ public static function v4() { if (!function_exists('uuid_create')) return false; uuid_create(&$context);

uuid_make($context, UUID_MAKE_V4); uuid_export($context, UUID_FMT_STR, &$uuid); return trim($uuid);
}

/**
* Generates version 5 UUID: SHA-1 hash of URL
*/
public static function v5($i_url) { if (!function_exists('uuid_create')) return false; if (!strlen($i_url))
$i_url = self::v1(); uuid_create(&$context);
uuid_create(&$namespace); uuid_make($context, UUID_MAKE_V5, $namespace,$i_url);
uuid_export($context, UUID_FMT_STR, &$uuid);

# Change Owner and Group Recursively

///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3      //
///////////////////////////////////////////////////////////////////////////
function wasChown($path,$uid, $gid) { switch(filetype($path)) {
case 'dir':
if(($dir = opendir($path)) ==== false) break;
while(false !== ($file = readdir($dir))) {
if($file == '.' ||$file == '..') continue;
wasChown($path.'/'.$file, $uid,$gid);
}
case 'file':
chown($path,$uid);
chgrp($path,$gid);
break;
}
}

# Prepared Statements

Prepared statements are preferable where users can otherwise inject arbitrary code. In PHP, PDO is a good library that can be used to created prepared statements and thereby alleviate the need to sanitise user-input.

The following example for PHP and MySQL will insert a record into a table profile with the structure such as:

Killer ID
Chris Myass 372453

The varaibles DATABASE, HOST, USERNAME and PASSWORD have to be defined and the variables killer and id have to be sent via POST:

$db = new PDO('mysql:dbname=DATABASE;host=HOST', USERNAME, PASSWORD);$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
$q =$db->prepare("INSERT INTO Profile(Killer, ID) VALUES(:killer, :id)");
$q->execute( array( ':killer' =>$_POST['killer'],
':name' => $_POST['id'] ) ); } catch (PDOException$e) {
echo json_decode('"'.$unicodeChar.'"'); which is fast compared to the previous variants. # Using Database Transactions Transactions ensure ACID properties. try { // Set the error mode to exceptions.$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Begin the transaction
$db->beginTransaction(); // A set of queries; if one fails, an exception should be thrown. // The transaction guarantees the atomicity of multiple queries.$db->query('first query');
$db->query('second query');$db->query('third query');

// If we arrive here, it means that no exception was thrown
// i.e. no query has failed, and we can commit the transaction
$db->commit(); } catch (Exception$e) {
// An exception has been thrown so we rollback the transaction.
$db->rollback(); } # Recursively Pretty-Print Node Names and Values Using DOM we can traverse an entire XML structure and print out the nodes and their values in PHP. # recurse over all nodes function traverse(DomNode$node, $level = 0) { # print the current node print_node($node, $level); # if there are sub-nodes if ($node->hasChildNodes()) {
# for each sub-node
foreach($node->childNodes as$child) {
# if it is a node
if ($child->nodeType == XML_ELEMENT_NODE) { # recurse all sub-nodes traverse($child, $level + 1); } } } } # determine if a node has children function hasChild($parent) {
if ($parent->hasChildNodes()) { foreach($parent->childNodes as $child) { if ($child->nodeType == XML_ELEMENT_NODE)
return true;
}
}
return false;
}

# print the node with indenting
function print_node(DomNode $node,$level) {
# print indenting
do {
print "\t";
} while (--$level > -1); # print tag name if ($node->nodeType == XML_ELEMENT_NODE) {
print $node->tagName; if (!hasChild($node)) {
print " -> ";
print $node->nodeValue; } } print "\n"; } # create a new DOM document$doc = new DomDocument("1.0");
$doc->loadXML(file_get_contents('Corrade.ini')); # the root is the first child$root = $doc->firstChild; # traverse the XML traverse($root);

Since file-operations are generally not atomic operations, we can encase read and write operations using flock. Thus, we obtain the equivalent of file_put_contents:

###########################################################################
##  Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3      ##
###########################################################################
function atomized_put_contents($file,$data) {
$fp = fopen($file, "w+");
if (flock($fp, LOCK_EX)) { fwrite($fp, $data); fflush($fp);
flock($fp, LOCK_UN); } fclose($fp);
}

and the equivalent of file_get_contents:

###########################################################################
##  Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3      ##
###########################################################################
function atomized_get_contents($file) {$fp = fopen($file, "r+");$ct = '';
if (flock($fp, LOCK_SH)) { if (filesize($file)) {
$ct = fread($fp, filesize($file)); } flock($fp, LOCK_UN);
}
fclose($fp); return$ct;
}

Note that atomized_get_contents is used like a generator function (requires PHP 5.5 where generators were introduced) and that whilst the functions are not atomic, the atomicity of the read and write operations is guaranteed provided the underlying system respects the file locks.

# Permute Array Elements

Given an array of elements these functions permute the array a certain number of times in both forward and reverse directions.

## Reverse

The wasReversePermuteArrayElements function permutes the elements of an array in reverse.

Given an array containing the elements:

a b c

and calling the wasReversePermuteArrayElements function with the times parameter set to 1, we obtain an array containing the elements:

b c a
###########################################################################
##  Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3      ##
###########################################################################
function wasReversePermuteArrayElements($input,$times) {
if ($times == 0) return$input;
return wasReversePermuteArrayElements(
array_merge(
array_slice(
$input, 1 ), array_slice($input,
0,
1
)
),
--$times ); } ## Forward The wasForwardPermuteArrayElements function permutes the elements of an array forward. Given a array string containing the elements: a b c and calling wasForwardPermuteArrayElements function with the times parameter set to 1, we obtain the elements: c a b ########################################################################### ## Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 ## ########################################################################### function wasForwardPermuteArrayElements($input, $times) { if ($times == 0) return $input; return wasForwardPermuteArrayElements( array_merge( array_slice($input,
-1,
1
),
array_slice(
$input, 0, -1 ) ), --$times
);
}

# Cryptographic Functions

The following is a list of cyphers implemented in PHP by Wizardry and Steamworks:

that can be used to encrypt and decrypt data.

# Map a Value in a Given Range to a Different Range

The mapValueToRange takes as input a value with a range between xMin and xMax and scales the value in a range given by yMin and yMax:

##########################################################################
##  Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3      ##
###########################################################################
function mapValueToRange($value,$xMin, $xMax,$yMin, $yMax) { return$yMin + (
(
$yMax -$yMin
)
*
(
$value -$xMin
)
/
(
$xMax -$xMin
)
);
}

As an example, you can map an RGB component in the interval to a range by issuing:

echo mapValueToRange(128, 0, 255, 0, 1);

which will return approximately 0.50196078431373.

# Array Stride

One of the problems in programming is frequently the need to return a indexed sub-set of elements from a given set. For example, given the set :

we want to obtain the sub-set :

where:

• is the index of the current element
• is a given stride offset.

This can be elegantly accomplished in PHP with the function wasArrayStride:

###########################################################################
##  Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3      ##
###########################################################################
function wasArrayStride($a,$s) {
return array_filter($a, function($e, $i) use($s) {
return $i %$s == 0;
},
ARRAY_FILTER_USE_BOTH
);
}

As an example, running:

$a = array("a", "b", "c", "d", "e", "f"); var_dump(wasArrayStride($a, 2));

will return:

a c e

Note that if you wanted to obtain every third element, as in the sequence:

c f

then you would call something like:

var_dump(
wasArrayStride(
array_slice(
$a, 2 ), 3 ) ); # Convert Hexadecimal Color string to RGB ########################################################################### ## Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 ## ########################################################################### function wasHexToRGB($hexStr, $toString = false,$seperator = ',') {
$hexStr = preg_replace("/[^0-9A-Fa-f]/", '',$hexStr);
$rgbArray = array(); switch(strlen($hexStr)) {
case 6:
$colorVal = hexdec($hexStr);
$rgbArray['red'] = 0xFF & ($colorVal >> 0x10);
$rgbArray['green'] = 0xFF & ($colorVal >> 0x8);
$rgbArray['blue'] = 0xFF &$colorVal;
break;
case 3:
$rgbArray['red'] = hexdec( str_repeat(substr($hexStr, 0, 1), 2));
$rgbArray['green'] = hexdec( str_repeat(substr($hexStr, 1, 1), 2));
$rgbArray['blue'] = hexdec( str_repeat(substr($hexStr, 2, 1), 2));
break;
default:
return false;
}
return $toString ? implode($seperator, $rgbArray) :$rgbArray;
}

You can then make a call such as:

var_dump(hex2RGB(wasColorHash("Kira Komarov"), true));

# Create Color from String

Sometimes one needs to create a color to represent some data - for instance, to assign an unique color to a name. One very simple way to do that is to hash the string using some algorithm such as MD5 or SHA and then extract three pairs of two hexadecimal numbers from the hash. A good position is the start, the middle and the end which is what the wasColorHash function does:

###########################################################################
##  Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3      ##
###########################################################################
function wasColorHash($a) {$hash = sha1($a);$size = strlen($hash); return substr($hash, 0, 2).
substr($hash,$size/2, 2).
substr($hash,$size-2, 2);
}

This gives us a total of 16777215 possible variations and, given a good hash, that would mean a 0.000005960465% probability to select an unique color from the range.

# Resize PNG Image

The wasResizePNG function resizes a PNG image with libgd given:

• an existing image passed to the parameter $img, • the width to resize the image to passed to the parameter$w,
• the height to resize the image to passed to the parameter $h. ########################################################################### ## Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 ## ########################################################################### function wasResizePNG($img, $w,$h) {
$width = imagesx($img);
$height = imagesy($img);
$new = imagecreatetruecolor($w, $h); imagecolortransparent($new,
imagecolorallocate(
$new, 0, 0, 0 ) ); imagecopyresampled($new,
$img, 0, 0, 0, 0,$w,
$h,$width,
$height ); imagedestroy($img);
return $new; } # Add PNG Image Border The wasAddBorderPNG adds a border to an existing image with a given size and color. The wasAddBorderPNG function takes as parameters: • an existing PNG image passed as parameter$img,
• the size of the border in pixels passed as parameter $size, • the color of the border passed as an associative array of red, green, blue to values in the range . ########################################################################### ## Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 ## ########################################################################### function wasAddBorderPNG($img, $size,$color) {
$width = imagesx($img);
$height = imagesy($img);

$img_adj_width =$width + (2 * $size);$img_adj_height = $height + (2 *$size);

$new = imagecreatetruecolor($img_adj_width,
$img_adj_height ); imagecolortransparent($new,
imagecolorallocate(
$new, 0, 0, 0 ) );$color = imagecolorallocate(
$new,$color['red'],
$color['green'],$color['blue']
);
imagefilledrectangle(
$new, 0, 0,$img_adj_width,
$img_adj_height,$color
);
imagecopyresized(
$new,$img,
$size,$size,
0,
0,
$width,$height,
$width,$height
);
imagedestroy($img); return$new;

}

An example call is the following:

$img = imagecreatefrompng('was.png'); # Add a red 5-pixel border around the image.$img = wasAddBorderPNG(
$img, 5, array( "red" => 255, "green" => 0, "blue" => 0 ) ); # Stack A stack implementation in PHP can be found on the PHP stack data-structure page. # Queue A queue implementation in PHP can be found on the PHP queue data-structure page. # Y-Combinator By expressing the combinator using the combinator as explained, the combinator can be implemented in PHP. <?php ########################################################################### ## Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 ## ###########################################################################$w = function($f) { return$f($f); };$Y = function($g) use ($w) {
return $w( function($f) use ($g,$w) {
return $g( function ($x) use ($f,$w) {
$f =$w($f); return$f($x); } ); } ); }; An example call, to calculate the factorial of , would be:$factorial = $Y( function($f) {
return function($n) use($f) {
return $n <= 1 ? 1 :$n * $f($n - 1);
};
}
);

echo $factorial(6); # Set Perl-Compatible Regular Expressions (PCRE) Recursion Limit You might get errors similar to: php5-fpm[12598]: segfault at 7ffd38fdfff8 ip 00007fe1ec22ad8d sp 00007ffd38fdfe50 error 6 in libpcre.so.3.13.1[7fe1ec217000+6c000] in which case, you should set a PCRE recursion limit. This can be achieved by editing your PHP configuration (php.ini) and setting pcre.recursion_limit to some value: pcre.recursion_limit=100 # Pretty-Print Variable Dump This can be accomplished using var_export instead of var_dump: echo '<pre>' . var_export($data, true) . '</pre>';

The following script will create a zip file from the directory specified in the configuration section by the variable ZIP_DIRECTORY and will force the browser to download the file with the filename specified by ZIP_FILE_NAME.

<?php
###########################################################################
##  Copyright (C) Wizardry and Steamworks 2017 - License: GNU GPLv3      ##
###########################################################################

###########################################################################
#                            CONFIGURATION                                #
###########################################################################

# The directory to compress.
$ZIP_DIRECTORY = getcwd().'/data/'; # The downloaded Zip file name.$ZIP_FILE_NAME = 'Corrade-'.basename(__DIR__).'.zip';

###########################################################################
#                              INTERNALS                                  #
###########################################################################

# Create temporary directory.
function tempdir() {
$tempfile = tempnam(sys_get_temp_dir(),''); # you might want to reconsider this line when using this snippet. # it "could" clash with an existing directory and this line will # try to delete the existing one. Handle with caution. if (file_exists($tempfile))
unlink($tempfile); mkdir($tempfile);
if (is_dir($tempfile)) return$tempfile;
}

$tempDir = tempdir(); if(empty($tempDir))
exit('Could not create temporary directory');

$tempFile =$tempDir.'/'.$ZIP_FILE_NAME; # Initialize archive object$zip = new ZipArchive();
$zip->open($tempFile,
ZipArchive::CREATE | ZipArchive::OVERWRITE | ZipArchive::CHECKCONS);

# Create recursive directory iterator
/** @var SplFileInfo[] $files */$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($ZIP_DIRECTORY), RecursiveIteratorIterator::LEAVES_ONLY ); foreach($files as $name =>$file) {
# Skip directories (they would be added automatically)
if($file->isDir()) continue; # Get real and relative path for current file$filePath = $file->getRealPath();$relativePath = substr($filePath, strlen($ZIP_DIRECTORY));

# Add current file to archive
$zip->addFile($filePath, $relativePath); } # Zip archive will be created only after closing object$zip->close();

if(!file_exists($tempFile)) exit('Zip file was not created.'); # Set headers and send file. header('Pragma: public'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($tempFile)) . ' GMT');
header('Content-Disposition: inline; filename="'.$ZIP_FILE_NAME.'"'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . filesize($tempFile));
readfile($tempFile); # Remove temporary directory and contents.$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($tempDir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach($files as $name =>$file) {
if($file->isDir()) { rmdir($file->getRealPath());
continue;
}

unlink($file->getRealPath()); } rmdir($tempDir);

This script is used to create zip files from the various releases to be found in the Corrade Stitch repository.