Awww it reminds me of 1996, HTML to match too 
I think whatever code your using to display your PHP is escaping quotes where it shouldn't too - see the password editor one.
Awww it reminds me of 1996, HTML to match too 
I think whatever code your using to display your PHP is escaping quotes where it shouldn't too - see the password editor one.
Well, take for example your 'Random 10-Digit Password Creator'. It screams "written by a newbie!" at me. bubblenut and myself have already given you suggestions to improve it, but these suggestions have been ignored.
The thing is, your website lacks good content, and the poor design doesnt help to alleviate that. It is far from the 'supposed to have a large collection of free PHP scripts' goal, and doesnt look worth contributing to since what it does have isnt worth using. This is turn makes the 'gets a good amount of traffic' sound rather dubious, even if it is true.
My suggestion is to stay around here and help out a little more. By helping out, you learn. By reading the suggestions made to help others, you improve too. You should also seek help in your existing scripts, get them improved and well formatted, and have more scripts in your current collection.
If a script works, why do you care how it's written?
If a script works, why do you care how it's written?
Well written (and documented) scripts are easier to understand, optimise and maintain.
phpstuff wrote:Well, I'm sure these forum posts are eventually deleted. If you put it on my site, it'll be there permanently.
Well, whether this site (and hence the posts on it) will outlive yours is of course one matter. But you're right about one thing. Posts that are in violation of these boards' Acceptable Use Policy (especially those that consist of advertising) do get deleted.
If a script works, why do you care how it's written?
If you didn't care how it was written, why did you post in the Code Critique forum and ask for comments? Or was it just so that you could boost your page ranking with another link here? Your motives are in question.
Roll an array $array[$first, ..., $mid, ..., $last] so that $mid is the first element. I.e., $array[$mid, ..., $last, $first, ...].
$array = array_reverse(array_merge(
array_reverse(array_slice($array, 0, $mid)),
array_reverse(array_slice($array, $mid))
));
EDIT: Should have given the description. Oops. Basically, take the first $mid elements of the array and reverse them. Take the rest of the array and reverse that. Stick the two chunks back together, then reverse the whole thing. (Missy Elliot helped me with this algorithm.)
Rolling [a,b,c,d,e,f,g,h,i] left (did I mention it was left?) by five places:
[a,b,c,d,e,f,g,h,i]
[a,b,c,d,e][f,g,h,i] slice
[e,d,c,b,a][i,h,g,f] reverse
[e,d,c,b,a,i,h,g,f] merge
[f,g,h,i,a,b,c,d,e] reverse
here is something i was just messing around with and for some reason i liked the way it look so here it is:
<html>
<head>
<style type="text/css">
body
{background-color:black; color:red; font-size: 15px; font-family: serif;}
p
{background-color: white; color:black; font-family:Verdana; font-size:15px;}
table
{background-color:black; color: green; font-size:15px; font-family:Verdana;}
</style>
</head>
<body>
Hey
<p class="p"> hey</p>
<table class="table">
<tr><td>hey</td><td>Whats</td></tr>
<tr><td>up</td><td>World??</td></tr>
</table>
</body>
</html>
Weedpacket wrote:Roll an array $array[$first, ..., $mid, ..., $last] so that $mid is the first element. I.e., $array[$mid, ..., $last, $first, ...].
My brain hurts now.
Wonderful stuff, per your usual specifications.
Thanks!
A reordering of the elements of an array can be described by listing the array indices those elements end up paired with in the reordered array. For example, if the array is array('a','b','c','d','e') and the list of indices is array(3,4,2,0,1), then the reordered array is array('d','e','c','a','b').
For this to be effective, the listing (the permutation) needs to contain the elements 0 through to n-1 inclusive exactly once each for an n-element array. Gaps or duplicates could cause problems.
So what if you're given a "permutation" that might suffer in spots from exactly these flaws? How to turn it into a proper permutation?
Well, if there are duplicates then there is more than one possible solution (each duplicate is a tie that could be broken either way) and in the absence of further information one's as good as any other. (If one's not as good as any other then you've got more work to do getting the permutation to reflect that before you get here.)
So. To turn an array that is supposed to be a permutation into an array that really is a permutation, closing up any gaps and effectively tossing a coin to decide how to order the duplicates:
asort($permutation);
$permutation = array_keys($permutation);
asort($permutation);
$permutation = array_keys($permutation);
Incidentally, if the original array is an ordinary numerically-indexed one, the first two lines produce a permutation, just not the one we want here. In fact, it's the inverse permutation, so if you ever find yourself in need of such a thing, this note supplies that, too. (The second two lines obviously therefore compute the inverse of the inverse
)
Something I came up with not too long ago for providing a means to express the difference between two times in terms of years, months, weeks, days, hours, minutes, seconds:
<?php
/**
* array timeDiff(int $t1, int $t2)
* $t1 and $t2 must be UNIX timestamp integers, order does not matter
* returns array broken down into years/months/weeks/etc.
*/
function timeDiff($t1, $t2)
{
if($t1 > $t2)
{
$time1 = $t2;
$time2 = $t1;
}
else
{
$time1 = $t1;
$time2 = $t2;
}
$diff = array(
'years' => 0,
'months' => 0,
'weeks' => 0,
'days' => 0,
'hours' => 0,
'minutes' => 0,
'seconds' =>0
);
foreach(array('years','months','weeks','days','hours','minutes','seconds')
as $unit)
{
while(TRUE)
{
$next = strtotime("+1 $unit", $time1);
if($next < $time2)
{
$time1 = $next;
$diff[$unit]++;
}
else
{
break;
}
}
}
return($diff);
}
// sample usage:
$start = strtotime('2007-01-15 07:35:55');
$end = strtotime('2009-11-09 13:01:00');
$diff = timeDiff($start, $end);
$output = "The difference is:";
foreach($diff as $unit => $value)
{
echo " $value $unit,";
}
$output = trim($output, ',');
echo $output;
?>
A little tool to create SQL create statements from an XMI entity relationship diagram. Useful if you design your databases in a tool like, for example, Umbrello.
So I'm flipping through one of my old programming texts (one of the ones in which I/O is principally done through punched cards) and come across an algorithm to find the intersection of two sets of numbers that is asymptotically faster than checking each element of one set to see if it appears in the other. (Remember through all this that the elements of a set are distinct, by definition; while array_intersect() will retain duplicate values, this won't.)
It is limited to integers only; but since these might be ID numbers for something more interesting, that's not a huge limitation.
It is noticeably faster than using array_intersect() once the sizes of the sets involved and the range of possible values climb above a couple of dozen or so. Below that, the extra overhead of having all the logic in bytecode instead of machine code dominates.
This is the algorithm. To find the intersection of two sets of integers A and B:
Multiply the values of A and B by 2
Add one to all the values of B
merge A and B into one list
sort the list (this is the bit that dominates the running time)
Walk through the sorted list; if 2n and 2n+1 are both elements of the list (if they are then, due to the sorting, they will be adjacent), then n is an element of both A and B.
And here's some code to share:
foreach($a as &$av) $av+=$av;
foreach($b as &$bv) $bv+=$bv+1;
unset($av, $bv);
$merge = array_merge($a,$b);
sort($merge);
$n = count($merge)-1;
$intersection = array();
for($i=0; $i<$n; $i++)
{
if(!($merge[$i]&1) && ($merge[$i+1]==$merge[$i]+1))
{
$intersection[] = $merge[$i++]>>1;
}
}
A potentially long chunk of text may be previewed on another page by exceprting the first n words or n characters to use as an abstract. When taking the first characters, ideally you don't want it to break off in mid-word (you don't want something containing "...she had a mandate to..." to be truncated to end with "...she had a man".)
So there are two ways to specify such a truncation: counting words or counting letters.
Here's a couple of functions for doing those. The first fetches the first n words from the string, while the second fetches as many words as it can short of exceeding a maximum of n characters.
Some points that both functions have in common:
"Word" is defined as "any consecutive series of non-whitespace characters (that can't be made any longer without including whitespace)". The string "a sentence fragment - and another." contains six words.
Leading and trailing whitespace is trimmed from the returned string.
Apart from what is trimmed, all whitespace in the returned string is preserved unchanged from the original.
The functions really only reassemble the words; a third function does all the splitting-into-words part for both.
The functions are poorly named.
function split_words($string)
{
return preg_split('/(?<=\S)(?=\s)/', ltrim($string));
}
function truncate_words($string, $wordcount)
{
return join('', array_slice(split_words($string), 0, $wordcount));
}
function truncate_words_to_length($string, $stringlength)
{
$bits = split_words($string);
$len = $i = 0;
while($len<=$stringlength)
{
$len += strlen($bits[$i++]);
}
return rtrim(join('', array_slice($bits, 0, $i-1)));
}
Just because I like one-liners:
function truncate_words_to_length($string, $length)
{
return(array_shift(explode(chr(0), wordwrap($string, $length, chr(0)))));
}

It's a common idiom (at least, it seems to be the standard approach in code I've seen):
$array = array();
loop
{
// Build $chunk array
$array = array_merge($array, $chunk);
}
But it's significantly faster to exploit the fact that [man]array_merge/man can take arbitrarily many arguments:
$array = array(array());
loop
{
// Build $chunk array
$array[] = $chunk;
}
$array = call_user_func_array('array_merge', $array);
When I say "significantly" here I'm talking orders of magnitude. Even if you're only merging two two-element arrays the latter method is already twice as fast, and the savings only improve as the number of arrays and the number of elements increase. I kid you not when I say that, On My Machineโข, merging 20,000 arrays was more than two thousand times slower when it was done consecutively than as a single twenty-thousand-argument call to array_merge().
[Edit: later... ] What's more: PHP 7's destructuring operator ... obviates the use of call_user_func_array: instead, the last line becomes
$array = array_merge(...$array);
[Edit: later still... ] From PHP 8, array_merge does the Right Thing when it's given no arguments (it returns an empty array). So the extra-nested array(array()) isn't needed any more, and can just be array(). Or, more concise yet, [].
Nothing earth-shaking here. Just something I threw together today because I've been using [man]parse_ini_file[/man] a lot lately, wanted to play with some PHP5 coding stuff, and to use phpdocumentor (the API documentation is online here on my site).
<?php
/**
* @author Charles Reace (www.charles-reace.com)
* @version 1.0
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
/**
* Read data from ini file and create a data array or object
*
* @link http://www.php.net/parse_ini_file See the parse_ini_file() documentation for info on the ini file syntax
*/
class IniFile
{
/**
* @var string relative or absolute path to the ini file
*/
private $iniFilePath = '';
/**
* Constructor
*
* @param string $path (optional) path to ini file
*/
public function __construct($path = '')
{
if($path != '')
{
$this->setIniFilePath($path);
}
}
/**
* Set the ini file path
*
* Throws exception on invalid path
*
* @param string $path path to the ini file
* @return boolean
*/
public function setIniFilePath($path)
{
if(is_readable($path))
{
$this->iniFilePath = $path;
return(TRUE);
}
else
{
throw new Exception("$path does not exist or is not readable.");
}
}
/**
* parse ini file and return data as an object
*
* throws exception if path not set
*
* @return object
*/
public function getIniDataObj()
{
if(empty($this->iniFilePath))
{
throw new Exception("No ini file path defined");
}
$dataArray = parse_ini_file($this->iniFilePath, TRUE);
$dataObj = new stdClass();
foreach($dataArray as $key => $value)
{
if(is_array($value))
{
foreach($value as $key2 => $value2)
{
$dataObj->$key->$key2 = $value2;
}
}
else
{
$dataObj->$key = $value;
}
}
return($dataObj);
}
/**
* parse ini file and return data as an array
*
* throws exception if path not set
*
* @return array
*/
public function getIniDataArray()
{
if(empty($this->iniFilePath))
{
throw new Exception("No ini file path defined");
}
return(parse_ini_file($this->iniFilePath, TRUE));
}
}
// sample usage:
try
{
$ini = new IniFile('path/to/config.ini');
$configDataObject = $ini->getIniDataObj();
printf("<pre>%s</pre>", print_r($configDataObject, 1));
}
catch(Exception $e)
{
user_error("<pre>$e</pre>");
}
I know this isn't Code Critique, but a few things occur to me NogDog:
Since you are returning the parsed ini file as a stdClass object, why not just allow IniFile object to be queried for parameters. So you'd just have to write $ini->$key, rather than $ini->getIniDataObj()->$key. A bit of __get magic would do the trick. You could use ArrayAccess and Iterator from the SPL to provide the array behaviour too.
In fact encapsulating array data is such a common task I have a class which does just that. I'll paste it here if you want a look.
Also how about IniReaderException rather than plain old Exception?
this is a little function i made for my site. basically it gets all the tutorial data that has been posted, checks to see if any tutorials with the same name already exist, and if so, what version it is. If there are already 5 versions then it wont allow any more. Its nothing special but it does the job
submit_tutorial.php file:
session_start();
/*********************************************************
**********************************************************
** /support/includes/scripts/submit_tutorial.php **
** ยฉ2007 Insight Computer Support **
** Version: 1.0 **
** Author: Tyrone Westall **
** Description: This script gathers the tutorial data**
** that is posted by the user, then sends it to the **
** main function **
**********************************************************
*********************************************************/
include('global_vars.php');
include('global_funcs.php');
include('global_funcs_index.php');
connect_db();
check_page_enabled("Tutorial Submission");
// Get incoming data
$user_posted = $_POST['user'];
$section_posted = $_POST['section'];
$title_posted = $_POST['title'];
$body_posted = $_POST['body'];
// Put values into a session just in case the user needs to be returned back to the tutorials page because of an error. This way they dont lose all the stuff they typed in
$_SESSION['user_temp'] = $user_posted;
$_SESSION['section_temp'] = $section_posted;
$_SESSION['title_temp'] = $title_posted;
$_SESSION['body_temp'] = $body_posted;
// Check to see if any of the boxes are empty
if(is_null($user_posted))
{
header("Location: http://www.insightpcs.net/support/?page=tutorials§ion=submit&errid=1&sid=$sid");
}
else if(is_null($section_posted))
{
header("Location: http://www.insightpcs.net/support/?page=tutorials§ion=submit&errid=2&sid=$sid");
}
else if(is_null($title_posted))
{
header("Location: http://www.insightpcs.net/support/?page=tutorials§ion=submit&errid=3&sid=$sid");
}
else if(is_null($body_posted))
{
header("Location: http://www.insightpcs.net/support/?page=tutorials§ion=submit&errid=4&sid=$sid");
}
else
{
// Add the tutorial to the database
tutorial_add($user_posted, $section_posted, $title_posted, $body_posted);
}
function called by above file
function tutorial_add($user_posted, $section_posted, $title_posted, $body_posted)
{
$user_posted = addslashes($user_posted);
$section_posted = addslashes($section_posted);
$title_posted = addslashes($title_posted);
$body_posted = addslashes($body_posted);
$user_posted = htmlentities($user_posted, ENT_QUOTES);
$section_posted = htmlentities($section_posted, ENT_QUOTES);
$title_posted = htmlentities($title_posted, ENT_QUOTES);
$body_posted = htmlentities($body_posted, ENT_QUOTES);
$date_time = $datetime;
// Check to see if there is a tutorial with the same title. If so, add (2) onto the end of it. If it has 2 on the end already, add 3, and so on and so forth until 5 is reached, then no more tutorials with that title can be used.
$statement = "SELECT * FROM site_tutorials WHERE tutorial_name='$title_posted'";
$query = mysql_query($statement) or die(mysql_error());
if(mysql_fetch_row($query))
{
$result = mysql_result($query, 0, "tutorial_version");
switch ($ver){
case ($result == "(Version 1)"):
$ver = "(Version 2)";
$array = array($title_posted, $ver);
$title = implode(" ", $array);
case ($result == "(Version 2)"):
$ver = "(Version 3)";
$array = array($title_posted, $ver);
$title = implode(" ", $array);
break;
case ($result == "(Version 3)"):
$ver = "(Version 4)";
$array = array($title_posted, $ver);
$title = implode(" ", $array);
break;
case ($result == "(Version 4)"):
$ver = "(Version 5)";
$array = array($title_posted, $ver);
$title = implode(" ", $array);
break;
case ($result == "(Version 5)"):
header("Location: http://www.insightpcs.net/support/?page=tutorials§ion=submit&errid=5&sid=$sid");
break;
}
// Create a separate statement to accomodate for the above possibility
$statement = "INSERT INTO site_tutorials VALUES ('', '$section_posted', '$title_posted', '$user_posted', '$body_posted', '$date_time', '$ver')";
mysql_query($statement) or die(mysql_error());
header("Refresh: 3; url=http://www.insightpcs.net/support/?page=tutorials§ion=submit&errid=none&sid=$sid");
}
else
{
// Tutorial doesnt exist, so make new tutorial
$statement = "INSERT INTO site_tutorials VALUES ('', '$section_posted', '$title_posted', '$user_posted', '$body_posted', '$date_time', '(Version 1)')";
mysql_query($statement) or die(mysql_error());
header("Refresh: 3; url=http://www.insightpcs.net/support/?page=tutorials§ion=submit&errid=none&sid=$sid");
}
unset($_SESSION['body_temp'], $_SESSION['title_temp']);
}
The array-traversal functions like array_map() and so on that take a callback function and an array and does something to the one with the other are nice, but they have a drawback in that there is no straightforward way to supply additional arguments to the callback. A lot of the time the only sensible approach is go back to using a loop.
array_map(), for example, lets you supply as many arguments as you like to the callback, but you have to pass arrays to array_map. Let's say you have an array of strings and you want to remove the first and last three characters from each of them. substr() is the obvious function of choice, but that only works on one string and we have a whole array. So what do we do?
The most obvious idea is to forgo the idea of using a traversal function:
foreach($array as &$string)
{
$string = substr($string, 3, -3);
}
Or maybe temporary arrays containing the other arguments duplicated the appropriate number of times (yuck):
$array = array_map('substr', $array,
array_fill(0,count($array), 3),
array_fill(0, count($array), -3));
Or a specialised callback function:
function substr_chop_ends($string)
{
return substr($string, 3, -3);
}
$array = array_map('substr_chop_ends', $array);
Perhaps using create_function:
$array = array_map(create_function('$string','return substr($string, 3, -3);'), $array);
But we're losing flexibility one way or the other with those last two: our callback function really can only trim the end characters off. If we want it to do something else we have to write another function.
Well, there is another approach (prepare to vom):
$GLOBALS['substrCallback_start'] = 3;
$GLOBALS['substrCallback_length'] = -3;
function substrCallback($string)
{
return strtr($string, $GLOBALS['substrCallback_start'], $GLOBALS['substrCallback_length']);
}
$array = array_map('substrCallback', $array);
But we won't go there.
The previous two approaches seem like the most practical, but what is needed is
a way to generate them as needed instead of having to hand craft every one.
$array = array_map(create_function('$string','return substr($string, '.$start.', '.$length.');'), $array);
But of course we might want to do this with other functions, with different sets of arguments; we might not even want our array to be the first argument. There still seems to be more hand-carving going on than necessary.
Enough with the introduction. Here's the code being shared.
$bindings = array();
function bind($f, $bound_args)
{
$GLOBALS["bindings"][$ref = count($GLOBALS["bindings"])] = array($f, $bound_args);
return create_function('', '$args = func_get_args();
list($f, $bound_args) = $GLOBALS["bindings"]['.$ref.'];
$args = array_combine(array_slice(array_diff_key(range(0,' . (max(array_keys($bound_args))-1) . '+count($args)),$bound_args),0,count($args)),$args) + $bound_args;
ksort($args);
return call_user_func_array($f, $args);'
);
}
The bind() function takes two arguments, the name of a callback function, and an array of values that are to be bound to the function's parameters, effectively pre-filling them in advance. In the ongoing example:
$chop_ends = bind('substr', array(1=>3, 2=>-3));
$array = array_map($chop_ends, $array);
The format of the $bound_args argument is not really that complicated. As you can see, it's an array. The indices of the array match up with the callback function's parameters (counting from 0), and the values as you might expect are the corresponding arguments. So "substr($string, $start, $length)", when bound with array(1=>3, 2=>-3), becomes "substr($string, 3, -3)".
The code works by basically doing just that. It uses a global array $GLOBALS['bindings'] (ugly, I know, but its contents need to be globally accessible even inside functions - an object-oriented version might be smarter but, apart from namespace niceness, not significantly different: the functions contained therein are still by definition global).
Because the values of $bound_args aren't necessarily serialisable (let alone representable as source code), they can't be embedded directly into the callback under construction (the number one most irritating thing about PHP's handling of runtime-generated functions). What's more, the function names generated by create_function aren't directly embeddable into the code of new created functions without a lot of arsing about with chr(0) and whatnot (no, wait, that's the most irritating thing).
So $GLOBALS['bindings'] stores both of these items (the name of the callback having its arguments bound and the bound arguments themselves) in such a way that they'll be accessible to the callback being constructed.
Then the callback is constructed. All that hairy long line setting $args does is merges $bound_args with any other arguments that might be passed to the callback when it is actually called in such a way that the bound arguments go where they should go and the passed arguments fill in the remaining spaces.
For example, consider the line
$blag = bind('wibble', array(4=>'foo', 2=>'bar'));
With that assignment, $blag becomes callable. Let's do that.
$blag('a','b','c','d','e');
What does that result in? Well, the function $blag takes the name of the callback ('wibble') and its bound arguments (array(4=>'foo', 2=>'bar')), merges with latter with the other arguments that have been passed (array('a','b','c','d','e')) and applies the callback to them. Result:
wibble('a','b','bar','c','foo','d','e');The thing to take away from that example is how the bound arguments and the passed arguments were merged. Note that "bar" is the second argument and "foo" the fourth (and "a" the zeroth).
Like I said, the whole point of the global $bindings array is that it is not necessary to use only callbacks and arguments that can be written down in PHP source code. The callback could be the name of a created function, or it could be an array that represents a class or object method; and the bound arguments could be pretty much anything that could be passed to a function (including objects and resources).
The global $bindings array is also its strongest weakness: anything that goes in that array stays in that array: it will sit around in there until the end of the script. So of course will the callback that each entry in the array is associated with.
Oh, one last thing. note that bind() itself only stores the bound arguments: they're used only when the created function is called. This matters for volatile things like objects and resources that may have had their state changed in the meantime.
And dribble off into some weedy examples...
function t1($a, $b, $c)
{
return $a+$b+$c;
}
$t3 = bind(bind('t1', array(3)), array(2));
echo $t3(42),"\t",$t3(1),PHP_EOL;
class Foo
{
static function bar($a,$b,$c)
{
return $a+$b+$c;
}
}
$t3 = bind(array('Foo','bar'), array(3));
$t3 = bind($t3, array(2));
echo $t3(42),PHP_EOL;
$t3 = bind(array('Foo','bar'), array(3,2));
echo $t3(42),PHP_EOL;
class Wibble
{
private $spink = 3;
function Womble($a,$b,$c)
{
return $a+$b+$c;
}
function screech()
{
$t3 = bind(array($this,'Womble'), array($this->spink, 2));
echo $t3(42);
}
}
$wibble = new Wibble;
$wibble->screech();
echo PHP_EOL;
Oh, and this entire post is obsolete as of PHP 5.3. So if you're using that version, reading this was a waste of your time
You'd write:
function chop_ends($start, $end)
{
return function($word)use($start, $end)
{
return substr($word, $start, $end);
};
}
$array = array_map(chop_ends(3, -3), $array);
[Edit much much later:] PHP 7.4 introduced arrow functions. Those automatically close over variables from the outer context that are explicitly used in the function body.
$start = 3;
$end = -3;
$array = array_map(fn($word) => substr($word, $start, $end), $array);
Bump
So you've got a grab-bag of items as an associative array; user preferences, say, or widget settings. You pass it around to various methods which may or may not be interested in some of the contents (you don't necessarily know what user preferences a given method might be interested in). The method rummages around in the bag to see if there are any items in there that it's interested in by looking at the keys for any that are applicable. The rest it ignores.
A common step at this point is to make a new array that contains only the relevant elements of the grab-bag. The following is a function to do exactly that; without requiring a foreach loop or a test or any other such traversal of the grab-bag's contents.
function select_by_keys($grabbag, $keys)
{
return array_intersect_key($grabbag, array_flip($keys));
}
To use:
// $options = array('size'=>'100px', 'fontNum'=>42, 'fontName'=>'bedrock', 'foo'=>'bar');
static $font_elements = array('fontName', 'fontNum', 'font');
$this->fonts[] = select_by_keys($options, $font_elements);
The new element of $this->fonts will be ['fontName'=>'bedrock', 'fontNum'=>42]. Because there was no $options['font'], there's no 'font' element in the new array either.
If you want defaults to be used instead, you'd basically have to supply them as well as the keys: that might as well be in the form of an associative array. And that's just a use of the [font=monospace]+[/font] operator.
static $defaults = array('fontName'=>'Helvetica')
$this->fonts[] = select_by_keys($options, $font_elements) + $defaults;