Lysender’s Daily Log Book

Life, Technology, Programming and Anything in Between

In Zend Framework, there are already tons of validators under Zend_Validate. In Zend_Form, you can attach validators, as a result, you can have a validator suite ready. However, I don’t use Zend_Form and I just don’t like how validations are used in Zend Framework. That’s why I’m creating my own custom validator suite that fits my style.

Mode of operation

This is how I usually process a submitted data via form POST.

  1. User submits form
  2. Controller extracts the posted data
  3. Validate each data that has validation rules (using simple if/else statement)
  4. Returns either boolean true or an array of error message and the name of the field to focus (first invalid field perhaps)
  5. Display the error messages
  6. Focus cursor on the first invalid field

If we are using Zend_Form, we can easily implement this, however, I don’t use Zend_Form, because I simply don’t like it. I’d like to put validations on the model than messing with the controller. Moreover, Zend_Form is too heavy and mostly not suited for our projects.

I’m really annoyed with the default error messages the Zend_Validate library has. Perhaps, my ultimate goal is to customize the error messages. Not translating it.

I created this validator suite with these goals in mind:

  • Validators are customizable and can be extended. I want my own error messages
  • Validators can validate one or more value (for example a pair of x and y coordinates)
  • Validators can refer another field (for example a password and a confirm password field)
  • A validator suite that can determine which fields have errors

The Codes

For me to bring this validation suite to other projects, I will put it into a single package which will be placed inside the library directory on Zend Framework directory structure. We can also put them somewhere else, as long as it is accessible via include path.

We will put all the validators and the validator suite under Dc/Validate. First, we will create a basic string validator. We will extend this validator for other string related validation rules. We will extend Zend_Validate_Abstract for our validation class Dc_Validate_String.

Dc_Validate_String (String.php)
Dc_Validate_String validates string using the minimum and maximum length rules. It has also support for setting encodings. It is based on Zend_Validate_StringLength. Some unnecessary parts are removed.

<?php

/**
 * Validation for regular strings
 *
 * @author lysender
 */
class Dc_Validate_String extends Zend_Validate_Abstract
{
    const INVALID   = 'stringInvalid';
    const TOO_SHORT = 'stringTooShort';
    const TOO_LONG  = 'stringTooLong';

    protected $_invalid = self::INVALID;
    protected $_tooShort = self::TOO_SHORT;
    protected $_tooLong = self::TOO_LONG;

    /**
     * @var string
     */
    protected $_key = 'string';

    /**
     * Message displayed when the key is missing from the class
     * $_key value
     *
     * @var string
     */
    protected $_missingKeyMessage = 'Validating string failed because of missing string key';

    /**
     * @var array
     */
    protected $_messageTemplates = array(
        self::INVALID   => "Value must be a string",
        self::TOO_SHORT => "Value must be greater than %min% character(s) long",
        self::TOO_LONG  => "Value must be less than %max% character(s) long"
    );

    /**
     * @var array
     */
    protected $_messageVariables = array(
        'min' => '_min',
        'max' => '_max'
    );

    /**
     * Minimum length
     *
     * @var integer
     */
    protected $_min = 0;

    /**
     * Maximum length
     *
     * If null, there is no maximum length
     *
     * @var integer|null
     */
    protected $_max;

    /**
     * Encoding to use
     *
     * @var string|null
     */
    protected $_encoding;

    /**
     * Sets validator options
     * $options is an array the either contains the following keys
     * min: minimum length
     * max: maximum length
     * encoding: encoding to use
     * key: the key of the value to ve validated
     *
     * @param  array $options
     * @return void
     */
    public function __construct(array $options = array())
    {
		foreach ($options as $opt => $val)
		{
			$key = '_' . $opt;
			if (isset($this->$key))
			{
				$func = 'set' . ucfirst($opt);
				$this->$func($val);
			}
		}
    }

    /**
     * Returns the key for the value
     *
     * @return string
     */
    public function getKey()
    {
    	return $this->_key;
    }

    /**
     * Sets the key for the value
     *
     * @param $key
     * @return this
     */
    public function setKey($key)
    {
    	if (!empty($key) && is_string($key))
    	{
    		$this->_key = $key;
    	}
    	return $this;
    }

    /**
     * Returns the min option
     *
     * @return integer
     */
    public function getMin()
    {
        return $this->_min;
    }

    /**
     * Sets the min option
     *
     * @param  integer $min
     * @throws Zend_Validate_Exception
     * @return this
     */
    public function setMin($min)
    {
        if (null !== $this->_max && $min > $this->_max)
        {
            throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum length, but $min >"
                                            . " $this->_max");
        }
        $this->_min = max(0, (integer) $min);
        return $this;
    }

    /**
     * Returns the max option
     *
     * @return integer|null
     */
    public function getMax()
    {
        return $this->_max;
    }

    /**
     * Sets the max option
     *
     * @param  integer|null $max
     * @throws Zend_Validate_Exception
     * @return this
     */
    public function setMax($max)
    {
        if (null === $max)
        {
            $this->_max = null;
        }
        else if ($max < $this->_min)
        {
            throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum length, but "
                                            . "$max < $this->_min");
        }
        else
        {
            $this->_max = (integer) $max;
        }

        return $this;
    }

    /**
     * Returns the actual encoding
     *
     * @return string
     */
    public function getEncoding()
    {
        return $this->_encoding;
    }

    /**
     * Sets a new encoding to use
     *
     * @param string $encoding
     * @return this
     */
    public function setEncoding($encoding = null)
    {
        if ($encoding !== null)
        {
            $orig   = iconv_get_encoding('internal_encoding');
            $result = iconv_set_encoding('internal_encoding', $encoding);
            if (!$result)
            {
                throw new Zend_Validate_Exception('Given encoding not supported on this OS!');
            }

            iconv_set_encoding('internal_encoding', $orig);
        }

        $this->_encoding = $encoding;
        return $this;
    }

    /**
     * Defined by Zend_Validate_Interface
     *
     * Returns true if and only if the string length of $value is at least the min option and
     * no greater than the max option (when the max option is not null).
     *
     * @param  string $value
     * @return boolean
     */
    public function isValid($value)
    {
    	if (!isset($value[$this->_key]))
    	{
    		throw new Zend_Validate_Exception($this->_missingKeyMessage);
    	}

        if (!is_string($value[$this->_key]))
        {
            $this->_error($this->_invalid);
            return false;
        }

        $this->_setValue($value[$this->_key]);
        if ($this->_encoding !== null)
        {
            $length = iconv_strlen($value[$this->_key], $this->_encoding);
        }
        else
        {
            $length = iconv_strlen($value[$this->_key]);
        }

        if ($length < $this->_min)
        {
            $this->_error($this->_tooShort);
        }

        if (null !== $this->_max && $this->_max < $length)
        {
            $this->_error($this->_tooLong);
        }

        if (count($this->_messages))
        {
            return false;
        }
        else
        {
            return true;
        }
    }
}

Each validator accept an array as parameter for the isValid() method. The array is an associative array, keys are the field name. This enables the validator to refer another field. To use the validator above, we will write:

$v = new Dc_Validate_string();
$data = ANY_ARRAY_SOURCE; // could be a filtered post
$check = $v->isValid($data);
if (!$check)
{
    var_dump($v->getMessages());
}

Note that getMessages() method is defined by the Zend_Validate_Abstract. Now, it is time to create a real validator. For example we have a field called name. Name must be between 1-60 characters for example. We will extend Dc_Validate_String to implement the rules. This time, our class will become shorter.

<?php 

/**
 * Validator for given name
 *
 * @author lysender
 */

class Dc_Validate_Name extends Dc_Validate_String
{
    const INVALID   = 'nameInvalid';
    const TOO_SHORT = 'nameTooShort';
    const TOO_LONG  = 'nameTooLong';

	protected $_invalid = self::INVALID;
	protected $_tooShort = self::TOO_SHORT;
	protected $_tooLong = self::TOO_LONG;

    /**
     * @var string
     */
    protected $_key = 'name';

    /**
     * Message displayed when the key is missing from the class
     * $_key value
     *
     * @var string
     */
    protected $_missingKeyMessage = 'Validating name failed because of missing name key';

    /**
     * @var array
     */
    protected $_messageTemplates = array(
        self::INVALID   => "Name must be a string",
        self::TOO_SHORT => "Name must be between %min% to %max% character(s) long",
        self::TOO_LONG  => "Name must be between %min% to %max% character(s) long"
    );

    /**
     * @var array
     */
    protected $_messageVariables = array(
        'min' => '_min',
        'max' => '_max'
    );

    /**
     * Minimum length
     *
     * @var integer
     */
    protected $_min = 1;

    /**
     * Maximum length
     *
     * @var integer
     */
    protected $_max = 60;
}

As you’ve noticed, only the error messages and the min/max rules are changed. That’s just the way it is. If we have several forms that needs to validate a name, we will use this validator. To use this:

$v = new Dc_Validate_Name;
$data = ANY_ARRAY_SOURCE; //could be POST
$check = $v->isValid($data);
if (!$check)
{
    var_dump($v->getMessages();
}

Now look, what if we want to validate a username for registration. A usename must be between 6-16 characters long and must be alpha numeric and underscores only. We can achieve this by extending Dc_Validate_Name and override isValid method so that it will validate the value as name first, then as username. For example as Dc_Validate_String_Username

public function isValid(array $value)
{
    if (!parent::isValid($value))
    {
        return false;
    }

    // you alpha numeric underscore validations here
}

If you want to validate username against a database, for example a username must be unique, then we will just create a new validator, extend Dc_Validate_String_Username and apply another rule. For example as Dc_Validate_Model_UserExists

public function isValid(array $value)
{
    if (!parent::isValid($value))
    {
        return false;
    }

    // your rule that checks if the username already exists
}

Beautiful isn’t it? At a cost of having a lot of files to load thus a performance issue especially if there are tons of fields to validate.

Next we will add another basic validator to validate integers.

<?php

/**
 * Number validator
 *
 * @author lysender
 */

class Dc_Validate_Num extends Zend_Validate_Abstract
{
    const NOT_NUMERIC  	= 'numNotNumeric';
    const TOO_SMALL		= 'numTooSmall';
    const TOO_LARGE 	= 'numTooLarge';

    protected $_notNumeric = self::NOT_NUMERIC;
    protected $_tooSmall = self::TOO_SMALL;
    protected $_tooLarge = self::TOO_LARGE;

    /**
     * Array key for the num value
     * @var string
     */
    protected $_key = 'num';

    /**
     * @var string
     */
    protected $_missingKeyMessage = 'Validating num failed because of missing num key';

    /**
     * @var int
     */
    protected $_min = 1;

    /**
     * @var int
     */
    protected $_max = 99999;

    /**
     * Validation failure message template definitions
     *
     * @var array
     */
    protected $_messageTemplates = array(
        self::NOT_NUMERIC   => "Must be a number",
        self::TOO_SMALL		=> "Number must be between %min% and %max%",
        self::TOO_LARGE		=> "Number must be between %min% and %max%"
    );

    /**
     * Additional variables available for validation failure messages
     *
     * @var array
     */
    protected $_messageVariables = array(
        'min' => '_min',
        'max' => '_max'
    );

    /**
     * Sets default option values for this instance
     *
     * @param array $options
     *
     * @return void
     */
    public function __construct(array $options = array())
    {
		foreach ($options as $opt => $val)
		{
			$key = '_' . $opt;
			if (isset($this->$key))
			{
				$this->$key = $val;
			}
		}

		if ($this->_min > $this->_max)
		{
			throw new Zend_Validate_Exception('Minimum must not be greater than maximum.');
		}
    }

    /**
     * Defined by Zend_Validate_Interface
     *
     * Returns true if and only if $value's num value is a valid number
     * $value must be an associative array containing num key
     *
     * @param  $value
     * @return boolean
     */
    public function isValid($value)
    {
    	if (empty($this->_key))
    	{
    		throw new Exception($this->_missingKeyMessage);
    	}

    	if (!is_array($value) || !isset($value[$this->_key]))
    	{
    		$this->_error($this->_missing);
    		return false;
    	}

    	$this->_setValue($value[$this->_key]);

    	if (!is_numeric($value[$this->_key]))
    	{
    		$this->_error($this->_notNumeric);
    		return false;
    	}

    	if ($value[$this->_key] < $this->_min)
    	{
    		$this->_error($this->_tooSmall);
    		return false;
    	}

    	if ($value[$this->_key] > $this->_max)
    	{
    		$this->_error($this->_tooLarge);
    		return false;
    	}

        return true;
    }
}

The purpose of Dc_Validate_Num is to validate if a value is a number and is between a range. Next, we will extend it to create a validator for order count. Order count must be a number between 1-126.

<?php

/**
 * Number validator
 *
 * @author lysender
 */

class Dc_Validate_OrderCount extends Dc_Validate_Num
{
    const NOT_NUMERIC  	= 'orderCountNotNumeric';
    const TOO_SMALL		= 'orderCountTooSmall';
    const TOO_LARGE 	= 'orderCountTooLarge';

    protected $_notNumeric = self::NOT_NUMERIC;
    protected $_tooSmall = self::TOO_SMALL;
    protected $_tooLarge = self::TOO_LARGE;

    /**
     * Array key for the num value
     * @var string
     */
    protected $_key = 'order_count';

    /**
     * @var string
     */
    protected $_missingKeyMessage = 'Validating order count failed because of missing order count key';

    /**
     * @var int
     */
    protected $_min = 0;

    /**
     * @var int
     */
    protected $_max = 126;

    /**
     * Validation failure message template definitions
     *
     * @var array
     */
    protected $_messageTemplates = array(
        self::NOT_NUMERIC   => "Order count must be a valid number",
        self::TOO_SMALL		=> "Order count must be between %min% and %max%",
        self::TOO_LARGE		=> "Order count must be between %min% and %max%"
    );

    /**
     * Additional variables available for validation failure messages
     *
     * @var array
     */
    protected $_messageVariables = array(
        'min' => '_min',
        'max' => '_max'
    );
}

They behaves just like our Dc_Validate_String example. Now, the most important part is the validator suite. We will call it Dc_Validate. It’s purpose is to create a validator suite that will allow the developer to add a validator against a field. Run all validations at once and extract error messages pointing to the field that has the errors.

<?php 

/**
 * Validator suite for forms and other validation
 * concerns
 *
 * @author lysender
 */
class Dc_Validate
{
   /**
     * Validator chain, each validator contains the instance,
     * the key for the field to be valdiated and the
     * breakChainOnFailure flag
     *
     * @var array
     */
    protected $_validators = array();

    /**
     * Array of validation failure messages
     * Each message contains the key of the field validated
     * and the list of messages
     *
     * @var array
     */
    protected $_messages = array();

    /**
     * Adds a validator to the end of the chain
     *
     * If $breakChainOnFailure is true, then if the validator fails, the next validator in the chain,
     * if one exists, will not be executed.
     *
     * @param  string                  $field
     * @param  Zend_Validate_Interface $validator
     * @param  boolean                 $breakChainOnFailure
     * @return Dc_Validate Provides a fluent interface
     */
    public function addValidator($field, Zend_Validate_Interface $validator, $breakChainOnFailure = false)
    {
        $this->_validators[] = array(
        	'field' => $field,
            'instance' => $validator,
            'breakChainOnFailure' => (boolean) $breakChainOnFailure
            );
        return $this;
    }

    /**
     * Returns true if and only if $value passes all validations in the chain
     *
     * Validators are run in the order in which they were added to the chain (FIFO).
     *
     * @param  mixed $value
     * @return boolean
     */
    public function isValid($value)
    {
        $this->_messages = array();
        $result = true;
        foreach ($this->_validators as $element)
        {
            $validator = $element['instance'];
            if ($validator->isValid($value))
			{
                continue;
            }
            $result = false;
            $messages = array('field' => $element['field'], 'messages' => $validator->getMessages());
            $this->_messages[] = $messages;
            if ($element['breakChainOnFailure'])
			{
                break;
            }
        }
        return $result;
    }

    /**
     * Returns array of validation failure messages
     *
     * @return array
     */
    public function getMessages()
    {
        return $this->_messages;
    }

    /**
     * Returns true if and only if there are messages
     * on the validator
     *
     * @return boolean
     */
    public function hasMessages()
    {
    	return !empty($this->_messages);
    }

    /**
     * Merges messages into a single dimensional array or
     * messages. The messages must be compatible with
     * Dc_Validate for it to work
     *
     * @param array $messages
     * @return array
     */
    public static function mergeMessages(array $messages)
    {
    	if (!empty($messages))
    	{
    		$result = array();
    		foreach ($messages as $msg)
    		{
    			foreach ($msg['messages'] as $fieldMessage)
    			{
    				$result[] = $fieldMessage;
    			}
    		}
    		return $result;
    	}
    	return false;
    }

    /**
     * Returns an array containing the first message and the
     * field name where the message came from
     *
     * @param array $messages
     * @return array
     */
    public static function getFirstMessage(array $messages)
    {
    	if (!empty($messages))
    	{
    		$result = array();
    		$firstMessage = reset($messages);

    		if (!isset($firstMessage['field']) || !isset($firstMessage['messages']))
    		{
    			return false;
    		}
    		$result['field'] = $firstMessage['field'];

    		$innerMessage = reset($firstMessage['messages']);
    		if (empty($innerMessage))
    		{
    			return false;
    		}

    		$result['message'] = $innerMessage;

    		return $result;
    	}
    	return false;
    }
}

It has two static methods, mergeMessages and getFirstMessage which deals with error messages. This is the class that we will use in the controller or model or wherever we want to put it.

For example we will put it on a controller just for testing and we will provide a sample post data. We write:

        $validator = new Dc_Validate;
        $validator->addValidator('name', new Dc_Validate_Name);
        // above will add a validator for the field "name"

        $validator->addValidator('username', new Dc_Validate_Name(
            array(
                'key' => 'username',
                'min' => 4,
                'max' => 16,
                'encoding' => 'utf-8'
            )
        ));

        // above provides a parameter to the name validator to customize its rules, instead of extending and creating a new class.

        $validator->addValidator('order_count', new Dc_Validate_OrderCount);
        $validator->addValidator('order_count_other', new Dc_Validate_OrderCount(
            array(
                'key' => 'order_count_other',
                'min' => 1,
                'max' => 10
            )
        ));
        // this time, number validators

        $data = array(
                    'name' => '',
                    'username' => 'dc',
                    'order_count' => 'abc',
                    'order_count_other' => 100
                );
        // sample data to be validated, imagine this is a $_POST variable

        if ($validator->isValid($data))
        {
            echo '<h2>Valid!';
        }
        else
        {
            $msg = $validator->getMessages();
            var_dump(Dc_Validate::mergeMessages($msg));

            echo '<hr />';
            echo '<h2>First message</h2>';
            var_dump(Dc_Validate::getFirstMessage($msg));
        }

        // now we used the validator suite.

So if we need more validators, we will just create more and make sure it is compatible with Dc_Validate.

Below is the sample output with xdebug enabled.

Get the full code on github: http://github.com/lysender/zf-custom-validator.

Before, I used SimpleTest for unit testing. However, given the more advanced PHP 5.xx features and coding standards, I completely switch to PHPUnit. Moreover, PHPUnit’s coding convention is similar to Zend Framework and PEAR.

Directory Structure

Since PHPUnit is already integrated to Zend Framework via Zend_Test packages, we will expect everything will work out of the box. Let us start with the directory structure. The suggested directory structure and samples on the web about setting up PHPUnit on Zend Framework does not work for me, perhaps there are some configurations missing. However, I just go on with my modifications.

Create a tests directory under the project directory, the same level as application, public and library as suggested by Zend Framework tutorials. The my style, create a directory under it, called TestSuites. Create a bootstrap file, phpunit.xml file and log directory. Under log directory, create a directory named coverage.

As you’ve noticed above, there are directories named application and library directories. That is because it is the one suggested on the tutorial. I didn’t removed them though they are all empty.

Next, we will create the bootstrap file. As you’ve seen on the image above, there are two bootstrap files, Bootstrap.php and BootstrapZend.php. Bootstrap.php was my first bootstrap but I created another one which is recommended by the tutorial. This is the content of BootstrapZend.php:

define('INDEX_PATH', str_replace('\\', '/', realpath(dirname(__FILE__) . '/../public')));
define('APPLICATION_PATH', str_replace('\\', '/', realpath(INDEX_PATH . '/../application')));
define('LIBRARY_PATH', str_replace('\\', '/', realpath(INDEX_PATH . '/../library')));
set_include_path(
		LIBRARY_PATH
		. PATH_SEPARATOR
		. get_include_path()
	);

/**
 * Salt for general hashing (security)
 */
define('GENERIC_SALT', 'asdDSasd4asdAd1GH4sdWsd1');

// Define application environment
define('APPLICATION_ENV', 'development');

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

It is just a simplified version of the regular index.php for most Zend Framework projects, trimmed down so that it will not dispatch the front controller.

Next, we need to configure phpunit.xml so that we can just run phpunit on this directory and that phpunit will execute tests automatically when tests are found under the TestSuites directory.

<phpunit colors="false">
    <testsuite name="ApplicationTestSuite">
        <directory>./TestSuites</directory>
    </testsuite>
    <filter>
        <whitelist>
            <directory suffix=".php">../application</directory>
            <exclude>
                <directory suffix=".phtml">../application/views</directory>
                <file>../application/Bootstrap.php</file>
            </exclude>
        </whitelist>
    </filter>
    <logging>
        <log type="coverage-html" target="./log/coverage" charset="UTF-8"
             yui="true" highlight="false" lowUpperBound="35" highLowerBound="70"/>
    </logging>
</phpunit>

The phpunit.xml file just configures the phpunit runtime so that it will look under TestSuites directory, exclude views scripts and store logs under logs/coverage.

As you’ve noticed, under TestSuites directory, there are directories named controllers, Fms and models. They mimic the directory structure under the application directory except that Fms comes from the library directory. It is just a way to organize our test cases although you can put all test cases on a single directory as you wish without modifying paths on some include statements.

Sample Test

Now let us try a sample test case. I have a sample test inside tests/TestSuites/models/CodeTest.php. Please note that all test cases must end with Test suffix.

require_once 'BootstrapZend.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';

class CodeTest extends Zend_Test_PHPUnit_ControllerTestCase
{
	public function setup()
	{
        // Assign and instantiate in one step:
        $this->bootstrap = new Zend_Application(
            'testing',
            APPLICATION_PATH . '/configs/application.ini'
        );
        parent::setUp();
	}

	public function testObject()
	{
		$codeModel = new Default_Model_Code;
		$this->assertType('Default_Model_Code', $codeModel);

		return $codeModel;
	}
    }

Take a look at the require_once statements. Instead of messing around require_once path, phpunit allows you to use the topmost path of your tests so that you will not have to resolve relative paths.

Instead of require_once ‘../../BootstrapZend.php’, we just use require_once ‘BootstrapZend.php’. To make it simple, we always refer to the path where phpunit.xml is located.

Since we have not activated autoloading feature of Zend Framework, we need to manually require the file ‘Zend/Test/PHPUnit/ControllerTestCase.php’ to before starting the main test case class.

Before any tests are run, we ensure that the Zend Framework environment is properly setup, eg: autoloading. That is why we have the setup() function in our sample test case. The actual test is done on all functions that has the test prefix, thus, in our example it is testObject.

$codeModel = new Default_Model_Code;
$this->assertType('Default_Model_Code', $codeModel);

The assertion above simply asserts that the object’s type must be a Default_Model_Code object.

Run Test

Open a console / command line / terminal / whatever you call it in your native OS / platform. Go to your tests directory and type “phpunit” assuming that phpunit is properly setup. You should get a result something like this:

The output above may show different to your tests because I run it against more than one test cases. Now, to create several test cases, you only need to put the require_once statements as written above and also don’t forget to include the setup function.

That’s it!

Slackware 13 and Bluetooth

Before Christmas 2009, I bought a USB Bluetooth Dongle from CDRKing and will be using it for transferring pictures from my wife’s cellphone to my laptop. With it is a Bluetooth Dongle and a CD driver installer for Windows. However, I will be using it on Slackware Linux as well.

So far it works great on Windows and I can use its full functionalities other than file transfer. But some work well be done to make it work on my Slackware Linux. Moreover, most of the functionalities will not work.

Get ObexPushD

ObexPushD is a very simple application for Linux that allows you to receive files via Bluetooth, IrDA or TCP. In this case, I will receive files from Bluetooth. Note that the application only supports receiving files and not sending.

Get ObexPushD from SlackBuilds. Here are some steps on how to install it. You need to install the SlackBuild package and the source. Please note that the source is not included on the SlackBuild package so you have to download it from the separate link provided on the SlackBuilds.org page.

1. Extract the slackbuild package (obexpushd.tar.gz)
2. Move the source code package into the obexpushd directory (the one you’ve recently extracted)
3. Run the slackbuild script as root

su
./obexpushd.SlackBuild

4. Install the SBo package (as root) created by the slackbuild script (the filename is displayed on your console once the slackbuild script finishes its process.

# file names may vary, anyway I used wildcards here
installpkg /tmp/obexpushd*SBo.tgz

5. Enable rc.Bluetooth on your /etc/rc.d directory (as root)

chmod +x /etc/rc.d/rc.Bluetooth

6. It is always safe to reboot the machine to apply changes so restart your computer now. The next time you log in to your Slackware Linux, you will not have Bluetooth ready.

7. To receive file, just plug the Bluetooth dongle and run this command as regular user. You must run this on a directory where you want your received files to be save. For example we want to save it under ~/bluetooth.

cd ~/bluetooth
obexpushd -n

That way, the command will stay running on the terminal so that you can see the file transfer activities. You don’t need to keep it running all the time so terminate it as soon as you’re finished with file transfer.

Of course there are other projects such as KBluetooth and the others but they are not yet available on Slackware.

Working Both GIT and SVN

SVN in the office, GIT at home. That is my current setup. How is it possible? Simple, GIT is so flexible that it can mingle with SVN very well. This is how I actually do it.

GIT at home

First, we have a running SVN central repository. To avoid conflict (although I could have done this the other way around) I exported the current project into a safe location. I bring it at home after office hours and setup a working directory out of the exported files.

cd /path/to/your/working/directory
git init

That will initialize an empty GIT repository on our project directory. Next we have to add the source codes and commit it.

git add .
git commit -m "Added project to repo."

Usually, we have some files or directories that are not needed to be under version control such as cache and log files. We will ignore them by creating a .gitignore file. Create a .gitignore file on the working directory, add and commit it afterwards. Below is a sample .gitignore file.

# Ignore Eclipse related config
.buildpath
.project
.settings

# Ignore dynamic / cache / log files
application/data/*
application/logs/*
application/tmp/*

After saving the file, add it to the repository:

git add .
git commit -m "Ignored some files."

If ever you have modified some files, you have to commit it afterwards. If you want to view a graphical representation of what has changed / modified, issue this command:

gitk

It will launch a window that shows the history of what you’ve done.

Clone repository

Now, what if we are going to bring the modified source code into the office? Easy. Grab your flash drive / thumb drive or external hard disk and clone your repository to it.

cd /path/to/your/portable/media
mkdir your-project-name
git clone /path/to/your/project_dir your-project-name
cd your-project-name
git remote rm origin

First, you change directory into your portable media directory (example: flash drive / thumb drive). Second, you create a directory for your project. Third, you clone your project into the newly created directory. Fourth, you change move inside the clone directory. Last, you removed the origin of the clone repository.

We removed the origin simple because it will always print an annoying message saying that you are ahead of xxx commit from your origin.

Combining with SVN

At the office, I will again create another repository. First, we will go to our SVN checkout directory and create a git repository there.

cd /path/to/your/svn/checkout/project
git init

You initialized your project directory which is also an SVN checkout directory. Before you add the files, we must first ignore all SVN related files. Create a .gitignore file and enter something like this:

# Ignore SVN files
.svn

Save the file and add it then commit.

git add .
git commit -m "Added project to repo. Ignoring svn files."

You should also ignore the .git and the .gitignore in your SVN so that they will not get added to SVN same as your SVN are not added to GIT. You can now work in your office SVN repo, add and commit. At the end of the day, you will commit all changes to your GIT repo on the same directory.

Now, we have 3 GIT repositories, one at home, one in the flash drive / thumb drive and one at the office. We need to synchronize the files by using the command git pull. First, plugin your flash drive / thumb drive.

Assuming that your current project directory (office repo) is at:

/var/www/htdocs/project-name

and your flash drive / thumb drive repo is at:

/media/YOURTHUMB/git-repo/project-name

then we will pull the changes from the flash drive and merge it on our office repo. This is how we will do it.

cd /var/www/htdocs/project-name
git pull /media/YOURTHUMB/git-repo/project-name

The command will fetch the changes on your thumb drive repo and merge it automatically to your office repo. If there are conflicts, resolved those conflicts, save and commit changes. In our example, the most common to have conflict is the .gitignore file. Just merge the content of the two conflicting files, save and commit it. However, it can automerge if the conflict does not occur on the same line.

The diagram below shows the workflow of synchronizing your office repo and your home repo.

From the office, we pull the changes to the flash drive / thumb drive. Then at home, we pull the changes from the flash drive to our home repo. When we do changes at home, we commit it. When we go back to work, before leaving the house, we pull the changes from the home repo into the flash drive. Arriving at the office, we pull the changes from the flash drive into our office repo.

Pretty isn’t it?

Using Array in Zend Config Ini

All of my projects in Zend Framework uses INI files as configuration files. They were just simple key=value pair. What if we want an array as value? Here, I am going to show how to implement an array in Zend_Config_Ini.

A sample INI file looks like this (aside from Zend Framework specific configurations):

# Floor settings
floor.width = 880
floor.height = 600
floor.marker.width = 25
floor.marker.height = 26
floor.marker.offset = 12

To create an array of values ,we will do something like this:

floor.marker.specialMarkers[] = 101
floor.marker.specialMarkers[] = 102
floor.marker.specialMarkers[] = 103

As usual, we get the configurations at bootstrap then save to registry with this:

		$config = new Zend_Config_Ini(
		    APPLICATION_PATH . '/configs/application.ini',
		    APPLICATION_ENV
		);

		//save to registry
		$registry = Zend_Registry::getInstance();

And finally access the configuration on our models/controllers. To get the configuration, we will get it from the registry:

# get from the registry
$config = Zend_Registry::get('config');

# to get the floor object
$floor = $config->floor;

# to get the floor width
$floorWidth = $floor->width;

# to get the marker height
$markerHeight = $floor->marker->height;

We stored the config object into the Zend_Registry so that we can access them within our Zend Framework application. The configuration object is a Zend_Config object. To use the array, we can do it like this:

# convert object to array
$specialMarkers = $floot->marker->specialMarkers->toArray();

# use the array
foreach ($specialMarkers as $val)
{
    echo "Special marker value: $val <br />";
}

First, we converted the special marker object into an array then assign it to a variable. Next we use the array into our application.

« Previous Entries  Next Page »