Powerful Tools to Quickly Skin an Application

The link above allows you to upload images, which can be compiled into a banner. The banner can then be run through robots-design. That will extract the colors and apply them to the CSS.

Free .PNG and .JPG Banner Builder

Allows you to upload up to 10 images.

Assembles them into a banner 100px high. Adjusts height, retains aspect ratio.

Produces both a .PNG and .JPG.

Generic Class Wrapper - 2

The code in the previous post was converted to use Zend Framework’s Db component for database access. One of the first things I noticed was that the code got smaller.

There are many ways to use Zend_Db, for this implementation, this approach fits well. Note the use of Zend_Config_Ini as well.

object.class.php changed, and item.class.php was converted to store the properties in an object, rather than an array. classes/db.class.php was deleted.

classes/object.class.php

<?php

require_once 'Zend/Config/Ini.php';
require_once 'Zend/Db.php';

Class Object
{
        private $db;

        public function __construct()
        {
                $Config = new Zend_Config_Ini('ini.php','database');
                $this->db = Zend_Db::factory($Config->database);
        }

        protected function load($sTable,$sIdName,$sId)
        {
                $this->db->setFetchMode(Zend_Db::FETCH_OBJ);
                $oResult = $this->db->fetchRow('SELECT * FROM `'.$sTable.'` WHERE `'.$sIdName.'` = ?', $this->db->quote($sId));
                return $oResult;
        }

        protected function save($sTable,$sIdName,$oArgs)
        {
                $sId=$oArgs->{$sIdName};
                unset($oArgs->{$sIdName});
                $aArgs=get_object_vars($oArgs);
                foreach ($aArgs as $k => $v)
                        $aArgs[$k]=$this->db->quote($v);
                if ($this->load($sTable,$sIdName,$sId))
                        $this->db->update($sTable,$aArgs,$sIdName.'='.$this->db->quote($sId));
                else
                        $this->db->insert($sTable,$aArgs);
                return $this->load($sTable,$sIdName,$sId);
        }

        protected function remove($sTable,$sIdName,$sId)
        {
                return $this->db->delete($sTable,$sIdName.'='.$db->quote($sId));
        }

        public function __destruct()
        {
                $this->db->closeConnection();
        }
}
?>

ini.php

<?php /*
[database]
database.adapter=pdo_mysql
database.params.host=localhost
database.params.dbname=dbname
database.params.username=username
database.params.password=password
*/ ?>

Generic Class Wrapper

The objective of this wrapper is to provide a streamlined interface to data stored in a database.

Notes

  • Object properties are stored in an array. This makes managing them must easier. Upon instantiation, the array is loaded with empty strings.
  • Magic methods are used to set and return properties.
  • The properties have a one-to-one, exact mapping to the underlying database.
  • A generic object exists beneath the specific object. It accepts a table name, identifier column name, and an array of values.

item.class.php

<?php
/*mysql> show full columns from items;
+---------+------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| Field   | Type             | Collation       | Null | Key | Default | Extra          | Privileges                      | Comment |
+---------+------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| item_id | int(10) unsigned | NULL            |      | PRI | NULL    | auto_increment | select,insert,update,references |         |
| name    | varchar(64)      | utf8_unicode_ci |      |     |         |                | select,insert,update,references |         |
| url     | varchar(128)     | utf8_unicode_ci |      | MUL |         |                | select,insert,update,references |         |
| text    | text             | utf8_unicode_ci |      |     |         |                | select,insert,update,references |         |
+---------+------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
4 rows in set (0.02 sec)
*/

require_once 'obj.class.php';
Class Item Extends Obj
{
        private $aData;

        public function __construct()
        {
                $this->aData=array_fill_keys(array('item_id','name','url','text'),'');
                parent::__construct();
        }

        public function __set($name, $value)
        {
                $this->aData[$name] = $value;
        }

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

        public function load($id)
        {
                $this->aData=parent::load('items','item_id',$id);
        }

        public function save()
        {
                return parent::save('items','item_id',$this->aData);
        }
}

?>

obj.class.php

<?php
require_once 'db.class.php';

Class Obj
{
        private $db;

        public function __construct()
        {
                $this->db=Database::singleton();
        }

        protected function load($sTable,$sIdName,$sId)
        {
                $aResult=false;
                $sQuery='SELECT * FROM `'.$sTable.'` WHERE `'.$sIdName.'`=\''.$this->db->SQLescape($sId).'\'';
                $this->db->query($sQuery);
                $aResult=$this->db->fetch_assoc();
                $this->db->free_result();
                return $aResult;
        }

        protected function save($sTable,$sIdName,$aArgs)
        {
                $sWhere=' WHERE `'.$sIdName.'`=\''.$this->db->SQLescape($aArgs[$sIdName]).'\'';
                unset($aArgs[$sIdName]);
                $sSet=$this->db->sPair($aArgs);
                $sQuery='SELECT * FROM `'.$sTable.'`'.$sWhere;
                $this->db->query($sQuery);
                if ($this->db->num_rows()>0)
                        $sQuery='UPDATE `'.$sTable.'` SET '.$sSet.$sWhere;
                else
                        $sQuery='INSERT INTO `'.$sTable.'` SET '.$sSet;
                $this->db->free_result();
                return $this->db->query($sQuery);
        }

        protected function remove($sTable,$sIdName,$sId)
        {
                $sQuery='DELETE FROM `'.$sTable.'` WHERE `'.$sIdName.'`=\''.$this->db->SQLescape($sId).'\'';
                return $this->db->query($sQuery);
        }

        public function __destruct()
        {
        }
}
?>

A search function will be added.

db.class.php

<?php
Class Database
{
        private static $instance;

        private $rLink;
        private $db;
        private $ini;
        private $rResult;

        public function __construct()
        {
                $this->ini = parse_ini_file('config.ini.php','true');
                $this->rLink = mysql_connect
                                ($this->ini['db']['host'],
                                 $this->ini['db']['user'],
                                 $this->ini['db']['password']);
                if ($this->rLink === false)
                        trigger_error(mysql_error());
                $this->db = mysql_select_db ($this->ini['db']['database'],$this->rLink);
                if ($this->db === false)
                        trigger_error(mysql_error());
                return true;
        }

        public static function singleton()
        {
                if (!isset(self::$instance)) {
                        $c = __CLASS__;
                        self::$instance = new $c;
                }
                return self::$instance;
        }

        public function SQLescape($s)
        {
                return mysql_real_escape_string($s,$this->rLink);
        }

        public function host_information()
        {
                return mysql_get_host_info($this->rLink);
        }

        public function query($sQuery)
        {
                $this->rResult=mysql_query($sQuery,$this->rLink);
                if ($this->rResult === false)
                        trigger_error(mysql_error());
                return $this->rResult;
        }

        public function fetch_assoc()
        {
                return mysql_fetch_assoc($this->rResult);
        }

        public function sPair($aPair)
        {
                $sReturn='';
                foreach ($aPair as $k => $v)
                        $sReturn .= "`$k`='".$this->SQLescape($v).'\', ';
                $sReturn=substr($sReturn,0,-2);
                return $sReturn;
        }

        public function sWhere($aWhere)
        {
                $sReturn='';
                foreach ($aWhere as $k => $v)
                        $sReturn .= " `$k`='".$this->SQLescape($v).'\' AND ';
                return substr($sReturn,0,-4);
        }

        public function sRegExp($aRegExp)
        {
                $sReturn='';
                foreach ($aRegExp as $k => $v)
                        $sReturn .= " `$k` REGEXP '".$this->SQLescape($v).'\' AND ';
                return substr($sReturn,0,-4);
        }

        public function num_rows()
        {
                return mysql_num_rows($this->rResult);
        }

        public function free_result()
        {
                mysql_free_result($this->rResult);
        }

        public function __destruct()
        {
                if (isset($this->rLink))
                        if ($this->rLink !== false)
                                mysql_close($this->rLink);
        }

        public function __clone()
        {
                trigger_error('Clone is not allowed.', E_USER_ERROR);
        }

}
?>

PHP Rating Demo

The basic idea of this rating script is to count the number of votes and the ratings and store them in a text file (a database could also be used).

The first step was to create some images to represent the ratings. The easiest approach was to use ImageMagick to create colorful asterisks.

Next, the javascript was written. There was one minor browser compatibility issue with IE, the margin was not included in the offset. A simple fix was to use PHP to synchronize the left margin in the style tag and to add it in later in the javascript, if it was IE.

Finally, the PHP was written and tested. It is listed below.

rating.php5

$sRatingFile='rating.txt';
if (is_file($sRatingFile))
        $sRating=file_get_contents($sRatingFile);
else
        $sRating="0,0";

$aRating=explode(',',$sRating);

if (isset($_GET['r']))
{
        $r=(int)$_GET['r'];
        if (($r<0)||($r>5)) die('Bad rating');
        else
        {
                $aRating[0]++;
                $aRating[1]+=$r;
                file_put_contents($sRatingFile,implode(',',$aRating));
        }
}
$v=$aRating[0];
if ($aRating[0]!=0)
        $r=(int)($aRating[1]/$aRating[0]);
else
        $r=0;

echo<<<INPUT
<p>
<label>Rating</label>
<input name="iRating" id="iRating" type="image" src="images/$r.rating.png" alt="Rating" onclick="ratingClick(event)" />
$v votes
</p>
<div style="display:none">
<img src="images/$r.rating.png" id="imgRating" />
</div>
INPUT;

In a production environment, some CAPTCHA or other tool should be used to prevent automated submissions.