5 Inspiring (and Useful) PHP Snippets
“X PHP Snippets” type articles abound on the Internet, so why write another one? Well, let’s face it… the PHP snippets in them are generally lame. Snippets that generating a random string or return $_SERVER["REMOTE_ADDR"]
for the IP Address of a client really aren’t that interesting and are of modest usefulness. Instead, here’s five snippets that you’ll no doubt find interesting and useful, presented along with the problems that inspired them. I hope the creativity in many of them inspire you to write better and more creative code in your own day-to-day endeavors.
1. Generating CSV
All too often we see code that attempts to convert a multi-dimensional array of data to CSV that looks like this:
<?php
$csv = "";
foreach ($data as $row) {
$csv .= join(",", $row) . "n";
}
echo $csv;
The problem is that the individual elements are not properly escaped, and a single value with either a quotation mark or comma can throw off a parser that later tries to consume the CSV data. It’s much better to use the built-in fputcsv()
function; it should execute faster because it’s implementation is in C code, and it handles the necessary quoting/escaping for you.
The following code wraps logic to construct CSV output from an array of data. It has optional parameters to allow for header columns and whether to flush the CSV directly to the browser or return the output as a string. The elegance here is the usage of streams with fputcsv()
as the function requires an open file handle to operate.
<?php
function toCSV(array $data, array $colHeaders = array(), $asString = false) {
$stream = ($asString)
? fopen("php://temp/maxmemory", "w+")
: fopen("php://output", "w");
if (!empty($colHeaders)) {
fputcsv($stream, $colHeaders);
}
foreach ($data as $record) {
fputcsv($stream, $record);
}
if ($asString) {
rewind($stream);
$returnVal = stream_get_contents($stream);
fclose($stream);
return $returnVal;
}
else {
fclose($stream);
}
}
With the toCSV()
function in your arsenal, generating CSV becomes easy and fool proof.
2. Autoloading Classes
Autoloading class files is common place, but maybe you don’t like some of the bloated, heavier autoloader offerings provided by various PHP frameworks, or maybe you just like to roll your own solutions. Luckily, it’s possible to roll your own minimal loader and still be compliant with the PSR-0 standard adopted by the PHP Standards Working Group, which I first demonstrated on my own blog.
The standard doesn’t describe what support functionality must be provided by a PSR-0 compliant autoloader (registration methods, configuration options, etc.). If it can automatically find a class definition in the <Vendor Name>(<Namespace>)
pattern, then it’s PSR-0 compliant. Furthermore, it doesn’t specify the parent directory for <Vendor Name>
. The extra “fluff” of most autoloader implementations is convenient if you need to specify the location via code, but is unnecessary if you simply use a directory already within PHP’s include path.
<?php
spl_autoload_register(function ($classname) {
$classname = ltrim($classname, "\");
preg_match('/^(.+)?([^\\]+)$/U', $classname, $match);
$classname = str_replace("\", "/", $match[1])
. str_replace(["\", "_"], "/", $match[2])
. ".php";
include_once $classname;
});
The magic here is in the regex which splits the incoming name into its constituent parts; the class name will always be in $match[2]
, and $match[1]
the namespace name which may or may not be an empty string. It’s necessary to identify the parts because the underscore has no special meaning in the namespace portion making a blind replace on underscores and backslashes incorrect.
3. Parsing Fixed-Width Data with unpack()
In today’s modern world filled with XML and JSON, you might think fixed-width formats are extinct… but you’d be wrong. There is still a large amount of fixed-width data, such as some log entries, MARC 21 (bibliographic information), NACHA (financial information), etc. And between you and me, I still have a soft spot for fixed-width data.
Fixed-width data is relatively easy to work with in languages like C because the data, one loaded into memory, aligns perfectly with the accessing data structure. But for some, working with fixed-data in a dynamic language like PHP can be a struggle; the loose typing of the language makes such memory access impossible. And as a result, we often see code that looks like this:
<?php
// Parse a NACHA header record
$row = fread($fp, 94);
$header = array();
$header["type"] = substr($row, 0, 1);
$header["priority"] = substr($row, 1, 2);
$header["immDest"] = substr($row, 3, 10);
$header["immOrigin"] = substr($row, 13, 10);
$header["date"] = substr($row, 23, 6);
$header["time"] = substr($row, 29, 4);
$header["sequence"] = substr($row, 33, 1);
$header["size"] = substr($row, 34, 3);
$header["blockFactor"] = substr($row, 37, 2);
$header["format"] = substr($row, 39, 1);
$header["destName"] = substr($row, 40, 23);
$header["originName"] = substr($row, 63, 23);
$header["reference"] = substr($row, 86, 8);
print_r($header);
You’re probably cringing. It’s okay, I wouldn’t want such code in my application either! It’s verbose and the indexing is error-prone. Luckily, there is a better alternative: unpack()
.
The documentation for unpack()
in the PHP Manual says: “Unpacks a binary string into an array according to the given format” and show usage examples escaped using binary data. What may not be immediately apparent is that the function can be used to parse fixed-width strings thanks to the format specifier ‘A’ which represents a character (after all, is not a string just a series of bits and bytes?).
Using unpack()
, the above example can be re-written more elegantly like so:
<?php
// Parse a NACHA header record
$row = fread($fp, 94);
$header = unpack("A1type/A2priority/A10immDest/A10immOrigin/"
. "A6date/A4time/A1sequence/A3size/A2blockFactor/A1format/"
. "A23destName/A23originName/A8reference", $row);
print_r($header);
The format string in this case is simply a series of A’s specifying character data, the character-count for the specific field, and the key name the retrieved data will be assigned in the final array, separated by slashes. A6date
for example parses out the 6 characters and makes them available as $header["date"]
.
4. Templating HTML
There’s never been much of a consensus about templating in the PHP community. We all agree that keeping HTML and PHP separate is desirable, but clash on the suitability of using template libraries such as Smarty or Twig. Some point out that PHP itself is a template engine, and argue against a library’s speed, syntax, etc. Others claim to have benefited greatly from using the DSL that templating systems provide. One compromise is to template your HTML to keep things clean using a very minimal class written in PHP.
<?php
class Template
{
protected $dir;
protected $vars;
public function __construct($dir = "") {
$this->dir = (substr($dir, -1) == "/") ? $dir : $dir . "/";
$this->vars = array();
}
public function __set($var, $value) {
$this->vars[$var] = $value;
}
public function __get($var) {
return $this->vars[$var];
}
public function __isset($var) {
return isset($this->vars[$var]);
}
public function set() {
$args = func_get_args();
if (func_num_args() == 2) {
$this->__set($args[0], $args[1]);
}
else {
foreach ($args[0] as $var => $value) {
$this->__set($var, $value);
}
}
}
public function out($template, $asString = false) {
ob_start();
require $this->dir . $template . ".php";
$content = ob_get_clean();
if ($asString) {
return $content;
}
else {
echo $content;
}
}
}
It’s not a full-fledged template engine; rather a succinct helper class that acts as a “bucket” to collects key/value data pairs which you can access in included files designated as templates. First you create an instance of the Template
class in your view, optionally passing a directory name used to look up the subsequent template files (allowing you to group related files). Then, provide the values that should populate the templates to either the set()
method or as a bare property. Once all the values are specified, you call the out()
method to render the template.
<?php
$t = new Template();
// setting a value as if it were a property
$t->greeting = "Hello World!";
// setting a value with set()
$t->set("number", 42);
// setting multiple values with set()
$t->set(array(
"foo" => "zip",
"bar" => "zap"
));
// render template
$t->out("mytemplate");
The mytemplate.php
file for the example might look something like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
...
</head>
<body>
<div role="main">
<h1><?=$this->greeting;?></h1>
...
</div>
</body>
</html>
Within the template files you have access to the full range of PHP functionality to format values, filter values, etc. as you see fit.
A second, optional parameter to out()
can specify to return the template contents as a string instead of flushing it directly to the browser, which you can take advantage of to replace placeholders in one template with the result of a previously filled template.
5. Using file_get_contents as a cURL Alternative
cURL is robust library for communicating over various protocols. It certainly is very full-featured, and there are times when nothing else will do. If you explicitly need functionality as exposed by cURL to accomplish your task, then use cURL! But the majority of day-to-day cURL usage in PHP revolves around issuing HTTP GET and POST requests, something that can be done easily with built-in PHP functions.
The problem with relying on cURL for issuing HTTP requests is two-fold: 1) there of often many options that need to be set, even for the simplest of transactions, and 2) it is an extension that may or may not be available depending on your hosting and installation situation; it is a common extension, but one that is not enabled by default.
file_get_contents()
and stream_context_create()
are two native PHP functions that have been available since the 4.3 days. Together, they can be used to perform many of the same types of requests commonly done via cURL.
For basic GET requests, file_get_contents()
can be used by itself:
<?php
$html = file_get_contents("http://example.com/product/42");
For requests where you need to specify HTTP headers, be it GET or any of the other HTTP methods, you can create a context by passing a specially-keyed array to stream_context_create()
and then pass the context to file_get_contents()
.
<?php
$context = stream_context_create(array(
"http" => array(
"method" => "POST",
"header" => "Content-Type: multipart/form-data; boundary=--foorn",
"content" => "--foorn"
. "Content-Disposition: form-data; name="myFile"; filename="image.jpg"rn"
. "Content-Type: image/jpegrnrn"
. file_get_contents("image.jpg") . "rn"
. "--foo--"
)
));
$html = file_get_contents("http://example.com/upload.php", false, $context);
The example above shows uploading a file via POST, with the context array specifying the necessary information for the transaction using the keys “method”, “header”, and “content”.
When using file_get_contents()
for complex requests such as file uploads, it can be helpful to first make a mock web form and run it through Firefox with firebug enabled or something similar and then inspect what was included in the request. From there you can deduce the important header elements to include.
Summary
Hopefully you’ve found the snippets presented in this article interesting. They showcase creative problem solving and using PHP’s built-in functionality to new effect. I hope you find them useful and inspiring. And if you have inspiring snippets of your own, feel free to share in the comments below.
Image via Fotolia