Category: "PHP"

Serializing Data to Pass between Perl and PHP

The objective of this task was to determine if data serialized by PHP could be decoded by Perl.

The first step was to create some serialized data.

In this case, the data is being used to define an interface. An associative array was used, with the first element serving to identify the type of data, and the second to contain the details of the interface. The details is an associative array where each element includes a validation string, label, help or error string, default value, and entered value. This could be extended to include i18n and l10n information, as well as a wide variety of other data.

The PHP code serializes the array, echos it, and then does a var_dump.

<?php
$aData=array(
'type'=>'Magic',
'details'=>array(
'url'=>array(
        'validation'=>'/^[\.\w\-]{1,255}$/',
        'label'=>'URL',
        'help'=>'Valid URL is letters, digits, dashes, periods',
        'default'=>'http://default.com',
        'value'=>'http://domain.com'),
'authid'=>array(
        'validation'=>'/^[\.\w\-]{1,255}$/',
        'label'=>'AuthId',
        'help'=>'Valid Id is letters, digits, dashes, periods',
        'default'=>'',
        'value'=>'')
));
$sSerialized=serialize($aData);
echo $sSerialized.PHP_EOL;
var_dump(unserialize($sSerialized));
echo PHP_EOL;

I took the serialized data echoed by PHP and pasted it into a Perl script.

It uses the PHP::Serialization module to unserialize the data. The code posted here is based on http://www.ohmpie.com/serialization, although this is a more limited example, the ohmpie.com page offers serveral differ serialization approaches.

The printAll method prints all the attributes and values for the class. Note that the values can be reached directly through the object.


#!/usr/bin/perl
# Thanks to: http://www.ohmpie.com/serialization/
use strict;
use PHP::Serialization;
use TestClass;
my $encoded='a:2:{s:4:"type";s:8:"Magic";s:7:"details";a:2:{s:3:"url";a:5:{s:10:"validation";s:19:"/^[\.\w\-]{1,255}$/";s:5:"label";s:3:"URL";s:4:"help";s:45:"Valid URL is letters, digits, dashes, periods";s:7:"default";s:18:"http://default.com";s:5:"value";s:17:"http://domain.com";}s:6:"authid";a:5:{s:10:"validation";s:19:"/^[\.\w\-]{1,255}$/";s:5:"label";s:6:"AuthId";s:4:"help";s:44:"Valid Id is letters, digits, dashes, periods";s:7:"default";s:0:"";s:5:"value";s:0:"";}}}';
my $data = PHP::Serialization::unserialize($encoded);
bless($data,'TestClass');
$data->printAll;

print "URL: ".$data->{'details'}->{'url'}->{'value'}."\n";

print "\n";

This is the TestClass package or module. It only includes the top two elements, type and details, PHP::serialize populates the object with the unserialize call.


#!/usr/bin/perl
# Thanks to: http://www.ohmpie.com/serialization/
#       http://www.perlhowto.com/iterate_through_a_hash
#       http://perl.about.com/od/packagesmodules/qt/perlcpan.htm
#       http://search.cpan.org/~bobtfish/PHP-Serialization-0.34/lib/PHP/Serialization.pm
package TestClass;
use strict; 

#The Constructor
sub new {

        my $obj = {
                type => undef,
                details => undef };
        bless($obj);

        return $obj;
}

sub printAll {
        my $key=undef;
        my %hash=undef;
        my $innerkey=undef;
        my %innerhash=undef;
        my $self=shift;
        my $value=undef;
        my $innervalue=undef;
        print "Type: " .
        $self->{'type'}."\n";
        %hash=%{$self->{'details'}};
        while (($key,$value) = each %hash )
        {
                print "key: $key\n";
                %innerhash = %{$value};
                while (($innerkey,$innervalue) = each %innerhash )
                {
                        print "\t$innerkey: $innervalue\n";
                }
        }
        print "\n";
}

1;

This approach allows data to be stored serialized in a database and read and updated by either Perl or PHP. The structure of the data can change, but the database schema would remain the same.

XML Fed Form Interface

This is a demonstration of how you can use XML to feed PHP to generate a form. It is helpful when a flexible form interface is needed. This example includes the name of the field, the length, a regex validation string, default value, label for the input, whether it is required or not, and error text. It could be extended have multilingual validation and error text.

<?xml version="1.0" encoding="utf-8" ?>
<interface>
        <name>Works</name>
        <fields>
                <field>
                        <name>URL</name>
                        <length>255</length>
                        <validation>[\w\.\-]{2,255}</validation>
                        <default>domain.com</default>
                        <label>URL</label>
                        <value>url.com</value>
                        <required>true</required>
                        <errortext>Letters, numbers, periods and dashes only</errortext>
                </field>
                <field>
                        <name>id</name>
                        <length>11</length>
                        <validation>[\d]{1,11}</validation>
                        <default>1</default>
                        <label>Id</label>
                        <required>true</required>
                        <errortext>Ids must be all digits</errortext>
                </field>
        </fields>
</interface>

This is the PHP that reads the XML. It uses SimpleXML, which is really nice.

<?php echo '<?xml version="1.0" encoding="utf-8" ?>' ?>
<?php
$xml=file_get_contents('bw.xml');
$xmldata = new SimpleXMLElement($xml);
$bValid=true;
/* Validate the submitted data */
if (isset($_POST['submit']))
{
        $bValid=true;
        foreach ($xmldata->fields->field as $f)
                if (isset($_POST["{$f->name}"]))
                {
                        $f->value=$_POST["{$f->name}"];
                        if (!preg_match('/^'.$f->validation.'$/',$_POST["{$f->name}"]))
                        {
                                $f->invalid=(bool)true;
                                $bValid=false;
                        }
                }
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>XML Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
label
{
font-weight:bolder;
}
input,label
{
display:block;
}
.invalid
{
color:#f00;
}
</style>
</head>
<body>
<h1>XML Form Sourcing Demo</h1>
<hr />
<h2>Generated Inputs</h2>
<!-- 
Generates the form from the XML data.  Note the use of the invalid flag for highlighting, and the display of
errortext if appropriate.
-->  
<form action="#" method="post">
<?php foreach ($xmldata->fields->field as $f) : ?>
<label <?php if (isset($f->invalid)) echo 'class="invalid"' ?> for="<?php echo $f->name?>"><?php echo (($f->required==
'true')?'*':'').$f->label ?>
<?php if (isset($f->invalid)) echo ' '.$f->errortext; ?>
<input name="<?php echo $f->name ?>" id="<?php echo $f->name ?>"
        maxlength="<?php echo $f->length ?>"
        value="<?php echo (isset($f->value)?$f->value:$f->default) ?>" />
</label>
<?php endforeach ?>
<br />
<input name="submit" type="submit" />
</form>
<h2>Raw XML Data</h2>
<!-- Display the raw XML data, for debugging/development -->
<blockquote><?php echo nl2br(htmlentities(file_get_contents('bw.xml'))) ?></blockquote>
<h2>Parsed XML</h2>
<!-- Display how SimpleXML parsed the XML, again for debugging/development -->
<?php
echo '<pre>';
var_dump($xmldata);
echo '</pre>';
?>
<h2>Updated XML (if valid)</h2>
<!-- Show how the XML was updated, if it was valid -->
<?php
if ($bValid)
        echo '<pre>'.nl2br(htmlentities($xmldata->asXML())).'</pre>';
?>
</body>
</html>

This is well-suited for applications which must use connection data into a variety of external interfaces. In this case, the data can be presented, entered, validated, and stored, then used later by a different process.

Great New Web Resource

CoderZone.org launched recently.

It’s great new resource for web people, from ‘n00bs’ to ‘w00ts’. What makes it special:

  • A great team of moderators. These guys are experienced and know the web.
  • A library of code snippets, little bits of code that will save you a tremendous amount of time. You can contribute code, too.
  • XHTML/HTML & CSS sandboxes so you can test out ideas quickly.
  • An SQL sandbox for testing queries.
  • It’s free.
  • A very cool design.
  • No ads, the forum is there to help people, not distract you with ads you aren’t going to click on anyway.

PHP Base Class Implementation with Iterator

This is a class definition for a base class which can be extended.

Features

  • Attributes are defined dynamically
  • Iteration is implemented, to allow the use of foreach
  • Attribute validation is included
  • Uses array_fill_keys if it is available, otherwise gracefully degrades to use a foreach loop
  • Allows attribute assignment with an associative array

Base Class Definition


require_once 'include/common.php';

class Base Implements Iterator
{
        /**      Location for overloaded data.  */
        protected $data = array();

        public function __construct($attribute_names=null,$attribute_values=null)
        {
                if ($attribute_names!==null)
                {
                        if (function_exists(array_fill_keys))
                                $this->data=array_fill_keys($attribute_names,'');
                        else
                                foreach ($attribute_names as $v)
                                        $this->data[$v]='';
                        if ($attribute_values!==null)
                                foreach ($attribute_values as $k => $v)
                                        $this->$k=$v;
                }
                else
                        if (get_class($this)!=='Base')
                                halt(FAIL_CLASS_HAS_NO_ATTRIBUTES,get_class($this).' class has no attributes');
        }

        public function __set($name,$value)
        {
                if (array_key_exists($name,$this->data))
                        $this->data[$name]=$value;
                else
                        halt(FAIL_INVALID_ATTRIBUTE,get_class($this).' invalid attribute '.$name);
        }

        public function __get($name)
        {
                if (array_key_exists($name,$this->data))
                        return($this->data[$name]);
                else
                        return null;
        }

        public function __isset($name)
        {
                return isset($this->data[$name]);
        }
        public function rewind()
        {
                return reset($this->data);
        }

        public function current()
        {
                return current($this->data);
        }

        public function key()
        {
                return key($this->data);
        }

        public function next()
        {
                return next($this->data);
        }

        public function valid()
        {
                return current($this->data)!==false;
        }


        public function __destruct()
        {
        }
}

Extension Class

class User extends Base
{
        public function __construct($aArgs=null)
        {
                bsBase::__construct(array(
                        'userID','roleName','userName',
                        'firstName','middleName','lastName','loginName',
                        'password','email','contactPhone'),$aArgs);
        }

        function __destruct()
        {
        }
}

Object Instantiation


/* Create a new user, with the userID set to 1 */
$oUser=new User(array('userID'=>1));
$oUser->firstName='John';
foreach ($oUser as $k => $v)
  echo $k.' -> '.$v.PHP_EOL;

This post courtesy of Lyrix, Inc. / Mobiso

An Ounce of Performance and Prevention

The Wicked Good Gallery was a lot of fun to build. It meets the stated requirements, I like the design, and I learned some good stuff.

One issue that always concerns me is performance. The Wicked Good Gallery demo has seven images, and on each page load, it reads the image directories, tests for a thumbnail and detailed image, and if they don’t exist, it creates them from the first .jpg in the directory it finds. The issue is that in most cases, when images are posted, they won’t change. This isn’t a versioned gallery, only the last image uploaded will be offered to site visitors.

For that reason, you can assume that the only time you need to test for new files is when the list of directories changes. You can further extend it to only rebuild new directories, but then you risk the problem of only testing for content in new directories, not refreshing any existing images.

Therefore, a simple caching solution was implemented. When the page loads, the code tests for the presence of a cache file. If the file is present, it compares the directories listed in the cache file to those it reads on the server. It they match, the cached list of artwork is used, otherwise, the cache is rebuilt.

$bUpdateCache=false;
$aDirectoryCache=array();
if (is_file($sDirectoryCache))
        include $sDirectoryCache;
else
        $bUpdateCache=true;
$aDirectories=glob($sArtworkDir.'/*');
if ($aDirectoryCache!=$aDirectories)
{
        foreach ($aDirectories as $k => $v)
        {
                if (is_file($v.'/'.$sArt) && is_file($v.'/'.$sThumb))
                        $aPieces[]=$v;
                else
                        if (make_images($v,$sArt,$iArtHeight,$sThumb,$iThumbHeight))
                                $aPieces[]=$v;
        }
        $bUpdateCache=true;
}
else
        $aDirectories=$aDirectoryCache;
if ($bUpdateCache)
        file_put_contents($sDirectoryCache,'<?php $aDirectoryCache='.var_export($aDirectories,true).';'.PHP_EOL.
                '$aPieces='.var_export($aPieces,true).'; ?>');

There was about a 0.0002 second improvement in performance. Although that sounds trivial, more rigorous testing, with more images, would likely justify the implementation. There is a performance penalty if the cache is removed, but it’s very small, since the cache is built with var_exports.

The prevention added was a base http://www.w3schools.com/TAGS/tag_base.asp tag. This tag ensures all files are drawn from the correct directories, while still allowing relative references in the code.


<base href="http://wirehopper.com/wickedgoodgallery/" />
<link rel="stylesheet" type="text/css" href="css/style.css" />

In this case, the base URL is http://wirehopper.com/wickedgoodgallery/, and the style tag will draw from http://wirehopper.com/wickedgoodgallery/css/style.css, regardless of any slashes submitted in the URI, the page will display properly, and future references, derived by page navigation (clicking on images and links) will be valid.