Password Validator

<?php

  /**
   * Squiloople Framework
   *
   * LICENSE: Feel free to use and redistribute this code.
   *
   * @author Michael Rushton <michael@squiloople.com>
   * @link http://squiloople.com/
   * @category Squiloople
   * @package Models
   * @subpackage Validators
   * @version 1.0
   * @copyright © 2011 Michael Rushton
   */

  // Define the namespace
  namespace Models\Validators;

  /**
   * Password Validator
   *
   * Hash or validate passwords
   */
  final class PasswordValidator
  {

    /**
     * The password
     *
     * @access private
     * @var string $_password
     */
    private $_password;

    /**
     * The salt
     *
     * @access private
     * @var string $_salt
     */
    private $_salt;

    /**
     * The pepper
     *
     * @access private
     * @var string $_pepper
     */
    private $_pepper = 'Sz^3X6r[UyvV~2]_0stT}8 uY7RwZx4{q|Q91W5';

    /**
     * Set the password
     *
     * @access public
     * @param string $password
     */
    public function __construct($password = '')
    {
      $this->_password = $password;
    }

    /**
     * Call the constructor fluently
     *
     * @access public
     * @static
     * @param string $password
     * @return \Models\Validators\PasswordValidator
     */
    public static function setPassword($password)
    {
      return new self($password);
    }

    /**
     * Return the password
     *
     * @access public
     * @return string
     */
    public function getPassword()
    {
      return $this->_password;
    }

    /**
     * Create a random password
     *
     * @access public
     * @return \Models\Validators\PasswordValidator
     */
    public function randomizePassword()
    {

      // If the password is incorrectly formed then randomize again
      if (!$this->isValid($this->_password = substr($this->getSalt(true), 0, 8)))
      {
        $this->randomizePassword();
      }

      // Reset the salt
      $this->_salt = null;

      // Return the object
      return $this;

    }

    /**
     * Validate the password
     *
     * @access public
     * @param bool|string $new
     * @return bool
     */
    public function isValid($new = false)
    {

      // The password must contain at least one character of each case, one digit, and be between 8 and 39 characters inclusive in length
      if (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[\x20-\x7E]{8,39}$/D', $new ?: $this->_password))
      {
        return false;
      }

      // Otherwise return true
      return true;

    }

    /**
     * Set the salt
     *
     * @access public
     * @param string $salt
     * @return \Models\Validators\PasswordValidator
     */
    public function setSalt($salt)
    {

      // Set the salt
      $this->_salt = $salt;

      // Return itself
      return $this;

    }

    /**
     * Generate and return a salt
     *
     * @access public
     * @param bool $reset
     * @return string
     */
    public function getSalt($reset = false)
    {

      // If a salt has been set and a reset is not required then return the stored salt
      if (!$reset && isset($this->_salt))
      {
        return $this->_salt;
      }

      // Reset the salt
      $salt = '';

      // Generate a random salt of 39 printable ASCII characters
      for ($i = 1; $i <= 39; ++$i)
      {
        $salt .= chr(mt_rand(32, 126));
      }

      // If the salt does not have the correct syntax then regenerate
      if (!$this->isValid($salt))
      {
        $this->getSalt(true);
      }

      // Return the random salt
      return $this->_salt = $salt;

    }

    /**
     * Return the pepper portion
     *
     * @access private
     * @return string
     */
    private function _getPepper()
    {
      return substr($this->_pepper, 0, 39 - strlen($this->_password));
    }

    /**
     * Hash the password
     *
     * @access private
     * @return string
     */
    private function _getHash()
    {
      return hash('sha384', $this->getSalt() . $this->_password . $this->_getPepper());
    }

    /**
     * Hash the password using an HMAC-inspired hash
     *
     * @access public
     * @return string
     */
    public function getHash()
    {
      return hash('sha512', substr(strrev($this->_pepper), 0, 20) . $this->_getHash());
    }

  }

Read More

Tags: ,

Saturday, February 19th, 2011 PHP No Comments

(Pseudo-) Database Class

This class is designed to be used with the MySQLi Classes provided in an earlier post

<?php

  /**
   * Squiloople Framework
   *
   * LICENSE: Feel free to use and redistribute this code.
   *
   * @author Michael Rushton <michael@squiloople.com>
   * @link http://squiloople.com/
   * @category Squiloople
   * @package Models
   * @subpackage Databases
   * @version 1.0
   * @copyright © 2011 Michael Rushton
   */

  // Define the namespace
  namespace Models\Databases;

  /**
   * Database
   *
   * Acts as a database for simple queries
   */
  final class Database
  {

    /**
     * An instance of \Models\MySQLi\MySQLiConnection
     *
     * @access private
     * @var \Models\MySQLi\MySQLiConnection $_connection
     */
    private $_connection;

    /**
     * An array of the data
     *
     * @access private
     * @var array $_data
     */
    private $_data = array();

    /**
     * Store an instance of \Models\MySQLi\MySQLiConnection
     *
     * @access public
     * @param string $hostname
     * @param string $username
     * @param string $password
     * @param string $database
     */
    public function __construct()
    {
      $this->_connection = call_user_func_array('\Models\MySQLi\MySQLiConnection::setConnection', func_get_args());
    }

    /**
     * Insert a row with the given data
     *
     * @access public
     * @param string $table
     * @return \Models\Databases\Database
     */
    public function insert($table)
    {

      // Prepare the statement
      $stmt = $this->_connection->prepareInsertStatement()
                ->addTable($table);

      // Prepare, bind, and execute the query
      $stmt = $this->_executeQuery($stmt, __FUNCTION__);

      // Store the new row's ID
      $this->_data['id'] = $stmt->getInsertID();

      // Return the \Models\Databases\Database object
      return $this;

    }

    /**
     * Delete the row with the given data
     *
     * @access public
     * @param string $table
     * @return \Models\Databases\Database
     */
    public function delete($table)
    {

      // Prepare the statement
      $stmt = $this->_connection->prepareDeleteStatement()
                ->addTable($table)
                ->addWhere('id = i:id');

      // Prepare, bind, and execute the query
      $this->_executeQuery($stmt, __FUNCTION__);

      // Return the \Models\Databases\Database object
      return $this;

    }

    /**
     * Update the row with the given data
     *
     * @access public
     * @param string $table
     * @return \Models\Databases\Database
     */
    public function update($table)
    {

      // Prepare the statement
      $stmt = $this->_connection->prepareUpdateStatement()
                ->addTable($table)
                ->addWhere('id = i:id');

      // Prepare, bind, and execute the query
      $this->_executeQuery($stmt, __FUNCTION__);

      // Return the \Models\Databases\Database object
      return $this;

    }

    /**
     * Select the rows with the given data
     *
     * @access public
     * @param string $table
     * @return \Models\Databases\Database
     */
    public function select($table)
    {

      // Prepare the select statement
      $stmt = $this->_connection->prepareSelectStatement()
                ->addTable($table);
                ->setLimit('1');

      // Prepare, bind, and execute the query
      $stmt = $this->_executeQuery($stmt, __FUNCTION__);

      // Store the row
      $this->_data = (array) current($stmt->getOne()->getData());

      // Free the result
      $stmt->freeResult();

      // Return the \Models\Databases\Database object
      return $this;

    }

    /**
     * Prepare, bind, and execute the query
     *
     * @access private
     * @param \Models\MySQLi\MySQLiStatement $stmt
     * @param string $function
     * @return \Models\MySQLi\MySQLiStatement
     */
    private function _executeQuery($stmt, $function)
    {

      // Iterate through the data
      foreach ($this->_data as $field => $value)
      {

        // Store the field and value in an array
        $parameters[] = $field . ':' . $value;

        // Convert the datatype into an initial or throw an exception if not valid for MySQL
        switch (gettype($value))
        {

          case ('integer'):
            $value = 'i';
            break;
          case ('double'):
            $value = 'd';
            break;
          case ('string'):
            $value = 's';
            break;
          case ('blob'):
            $value = 'b';
            break;
          default:
            throw new \Exception('Unknown data type for MySQL query');

        }

        // If the function is "select" then add a WHERE clause
        if ($function == 'select')
        {
          $stmt->addWhere($field . ' = ' . $value . ':' . $field);
        }

        // Otherwise if the function is not "delete" and the field is not "id" then add a SET clause
        elseif ($function != 'delete' && $field != 'id')
        {
          $stmt->addValue($field, $value . ':' . $field);
        }

      }

      // Prepare the query
      $stmt = $stmt->prepareQuery();

      // Set the values of the bound parameters
      call_user_func_array(array($stmt, 'setParameters'), $parameters);

      // Return the \Models\MySQLi\MySQLiResult object
      return $stmt;

    }

    /**
     * Set the data
     *
     * @access public
     * @param string $field
     * @param mixed $value
     */
    public function __set($field, $value)
    {
      $this->_data[$field] = $value;
    }

    /**
     * Get the field data
     *
     * @access public
     * @param string $field
     * @return mixed
     */
    public function __get($field)
    {

      // If the field data is not present then return null
      if (!isset($this->_data[$field]))
      {
        return null;
      }

      // Return the field data
      return $this->_data[$field];

    }

  }

Read More

Tags: ,

Thursday, January 27th, 2011 PHP No Comments

BBCode Parser

<?php

  /**
   * Squiloople Framework
   *
   * LICENSE: Feel free to use and redistribute this code.
   *
   * @author Michael Rushton <michael@squiloople.com>
   * @link http://squiloople.com/
   * @version 1.0
   * @category Squiloople
   * @package Models
   * @subpackage Parsers
   * @copyright © 2010 Michael Rushton
   */

  // Define the namespace
  namespace Models\Parsers;

  /**
   * BBCode Parser
   *
   * Parses BBCode in a string
   */
  final class BBCodeParser
  {

    /**
     * Array to contain regular expressions of BB tags
     *
     * @access private
     * @var array $_bbTags
     */
    private $_bbTags = array(

      '/\[b\]([\x20-\x7E]+?)\[\/b\]/i',
      '/\[i\]([\x20-\x7E]+?)\[\/i\]/i',
      '/\[u\]([\x20-\x7E]+?)\[\/u\]/i',
      '/\[s\]([\x20-\x7E]+?)\[\/s\]/i',
      '/\[sub\]([\x20-\x7E]+?)\[\/sub\]/i',
      '/\[sup\]([\x20-\x7E]+?)\[\/sup\]/i',
      '/\[img\]([\x20-\x7E]+?)\[\/img\]/i',
      '/\[url\]([\x20-\x7E]+?)\[\/url\]/i',
      '/\[email\]([\x20-\x7E]+?)\[\/email\]/i',
      '/\[quote\]([\x20-\x7E]+?)\[\/quote\]/i',
      '/\[color=([0-9a-f]{6})\]([\x20-\x7E]+?)\[\/color\]/i',
      '/\[size=([1-9]?[0-9])\]([\x20-\x7E]+?)\[\/size\]/i',
      '/\[font=([a-z\x20]+)\]([\x20-\x7E]+?)\[\/font\]/i',
      '/\[img=([\x20-\x7E]+?)\]([\x20-\x7E]+?)\[\/img\]/i',
      '/\[url=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/url\]/i',
      '/\[email=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/email\]/i',
      '/\[quote=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/quote\]/i',

    );

    /**
     * Array to contain HTML tag replacements
     *
     * @access private
     * @var array $_htmlTags
     */
    private $_htmlTags = array(

      '<strong>$1</strong>',
      '<em>$1</em>',
      '<span style="text-decoration:underline">$1</span>',
      '<del>$1</del>',
      '<sub>$1</sub>',
      '<sup>$1</sup>',
      '<img src="$1" alt="" />',
      '<a href="$1">$1</a>',
      '<a href="mailto:$1">$1</a>',
      '<fieldset>$1</fieldset>',
      '<span style="color:#$1;background-color:transparent">$2</span>',
      '<span style="font-size:$1px">$2</span>',
      '<span style="font-family:\'$1\', sans-serif">$2</span>',
      '<img src="$2" alt="$1" />',
      '<a href="$1">$2</a>',
      '<a href="mailto:$1">$2</a>',
      '<fieldset><legend>$1</legend>$2</fieldset>',

    );

    /**
     * Create "[tag]Text[/tag] => <tag>Text</tag>" style BB tags
     *
     * @access public
     * @param string $bbTag
     * @param string|bool $htmlTag
     * @return array
     */
    public function createTag($bbTag, $htmlTag = false)
    {

      // If an HTML tag is not specified, emulate the BB tag
      $htmlTag = $htmlTag ?: $bbTag;

      // Create a new BB tag regular expression of the form: [tag]Text[/tag]
      $this->_bbTags[] = '/\[' . $bbTag. '\]([\x20-\x7E]+?)\[\/' . $bbTag . '\]/i';

      // Create a new HTML tag replacement of the form Text
      $this->_htmlTags[] = '<' . $htmlTag . '>$1</' . strtok($htmlTag, ' ') . '>';

      // Return an array with the BBCode regular expression and the HTML replacement
      return array(end($this->_bbTags), end($this->_htmlTags));

    }

    /**
     * Create "[tag=option]Text[/tag] => <tag option="option">Text</tag>" style BB tags
     *
     * @access public
     * @param string $bbTag
     * @param string $htmlTag
     * @return array
     */
    public function createParameterTag($bbTag, $htmlTag)
    {

      // Create a new BB tag regular expression of the form [tag=option][/tag]
      $this->_bbTags[] = '/\[' . $bbTag . '=([\x20-\x5A\x5C\x5E-\x7E]+)\]([\x20-\x7E]+?)\[\/' . $bbTag . '\]/i';

      // Create a new HTML tag replacement of the form Text
      $this->_htmlTags[] = '<' . $htmlTag . '>$2</' . strtok($htmlTag, ' ') . '>';

      // Return an array with the BBCode regular expression and the HTML replacement
      return array(end($this->_bbTags), end($this->_htmlTags));

    }

    /**
     * Create "[smile] => :)" style BB tags
     *
     * @access public
     * @param string $bbTag
     * @param string $htmlTag
     * @return array
     */
    public function createSpecialTag($bbTag, $htmlTag)
    {

      // Create a new BB tag regular expression of the form [tag] (does not require usual brackets)
      $this->_bbTags[] = '/' . preg_quote($bbTag) . '/i';

      // Create a new replacement (does not need to be an HTML tag)
      $this->_htmlTags[] = $htmlTag;

      // Return an array with the BBCode regular expression and the HTML replacement
      return array(end($this->_bbTags), end($this->_htmlTags));

    }

    /**
     * Return the parsed string
     *
     * @access public
     * @param string $string
     * @return string
     */
    public function parseString($string)
    {

      // Only replace tags if the string has not been fully parsed
      for ($count = 1; $count != 0;)
      {
        $string = preg_replace($this->_bbTags, $this->_htmlTags, $string, -1, $count);
      }

      // Return the parsed string
      return $string;

    }

  }

Read More

Tags: ,

Tuesday, November 2nd, 2010 PHP No Comments

MySQLi Classes

MySQLi Connection

<?php

  /**
   * Squiloople Framework
   *
   * LICENSE: Feel free to use and redistribute this code.
   *
   * @author Michael Rushton <michael@squiloople.com>
   * @link http://squiloople.com/
   * @category Squiloople
   * @package Models
   * @subpackage MySQLi
   * @version 1.0
   * @copyright © 2010 Michael Rushton
   */

  // Define the namespace
  namespace Models\MySQLi;

  /**
   * MySQLiConnection
   *
   * Control a MySQLi connection
   */
  final class MySQLiConnection
  {

    /**
     * An instance of the class
     *
     * @access private
     * @static
     * @var \Models\MySQLi\MySQLiConnection|null $_instance
     */
    private static $_instance;

    /**
     * The connection resource
     *
     * @access private
     * @var mysqli $_connection
     */
    private $_connection;

    /**
     * Try to connect to the server
     *
     * @access private
     * @param array $connect
     */
    private function __construct($connect)
    {

      // If a connection cannot be made then throw an exception
      if (!$this->_connection = call_user_func_array('mysqli_connect', $connect))
      {
        throw new \Exception('Unable to connect to the database server');
      }

    }

    /**
     * Try to create an instance of the object, and then return it
     *
     * @access public
     * @static
     * @param string $hostname
     * @param string $username
     * @param string $password
     * @param string $database
     * @return \Models\MySQLi\MySQLiConnection
     */
    public static function setConnection()
    {

      // If an instance has not been created then create one
      if (!isset(self::$_instance))
      {
        self::$_instance = new self(func_get_args());
      }

      // Return the \Models\MySQLi\Connection instance
      return self::$_instance;

    }

    /**
     * Return the connection
     *
     * @access public
     * @return mysqli|false
     */
    public function getConnection()
    {
      return $this->_connection;
    }

    /**
     * Try to select the database
     *
     * @access public
     * @param string $database
     * @return \Models\MySQLi\MySQLiConnection
     */
    public function setDatabase($database)
    {

      // If the database cannot be selected then throw an exception
      if (!$this->_connection->select_db($database))
      {
        throw new \Exception('Unable to select the database');
      }

      // Return the \Models\MySQLi\MySQLiConnection instance
      return $this;

    }

    /**
     * Try to prepare the query
     *
     * @access public
     * @param string $query
     * @param string $result
     * @return \Models\MySQLi\MySQLiResult
     */
    public function prepareQuery($query, $result = '')
    {

      // Store all the matches to be used for bound parameters
      preg_match_all('/[idsb]:([a-zA-Z0-9_$]+|`[^`]+`)/', $query, $vars);

      // If the re-formatted query cannot be prepared then throw an exception
      if (!$stmt = $this->_connection->prepare(preg_replace('/[idsb]:([a-zA-Z0-9_$]+|`[^`]+`)/', '?', $query)))
      {
        throw new \Exception('Unable to prepare the query');
      }

      $result = '\Models\MySQLi\MySQLi' . ucfirst(strtolower($result)) . 'Result';

      // Instantiate and return the \Models\MySQLi\MySQLiResult object
      return new $result($stmt, $vars[0]);

    }

    /**
     * Prepare an INSERT statement
     *
     * @access public
     * @return \Models\MySQLi\MySQLiInsertStatement
     */
    public function prepareInsertStatement()
    {
      return new MySQLiInsertStatement;
    }

    /**
     * Prepare a DELETE statement
     *
     * @access public
     * @return \Models\MySQLi\MySQLiDeleteStatement
     */
    public function prepareDeleteStatement()
    {
      return new MySQLiDeleteStatement;
    }

    /**
     * Prepare an UPDATE statement
     *
     * @access public
     * @return \Models\MySQLi\MySQLiUpdateStatement
     */
    public function prepareUpdateStatement()
    {
      return new MySQLiUpdateStatement;
    }

    /**
     * Prepare a SELECT statement
     *
     * @access public
     * @return \Models\MySQLi\MySQLiSelectStatement
     */
    public function prepareSelectStatement()
    {
      return new MySQLiSelectStatement;
    }

    /**
     * Try to disconnect from the server
     *
     * @access public
     */
    public function disconnect()
    {

      // If the server connection cannot be closed then throw an exception
      if (!$this->_connection->close())
      {
        throw new \Exception('Unable to close the connection');
      }

      // Destroy the instance
      self::$_instance = null;

    }

    /**
     * Throw an exception if a clone is attempted
     *
     * @access public
     */
    public function __clone()
    {
      throw new \Exception('Attempt to clone ' . __CLASS__);
    }

    /**
     * Disconnect from the server
     *
     * @access public
     */
    public function __destruct()
    {
      $this->disconnect();
    }

  }

Read More

Tags: ,

Wednesday, September 22nd, 2010 PHP 2 Comments

IP Address Validation

An IP address is one of an IPv4 address, an IPv6 address, or an IPv4-mapped IPv6 address.

An IPv4 address consists of four groups, separated by dots, each containing a decimal value between 0 and 255. A regular expression check to match for an IPv4 address would be as follows:

// IPv4 address

'/^(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?1)){3}$/D'

Read More

Tags: ,

Monday, August 16th, 2010 PHP 1 Comment

Email Address Validation

Email addresses have a local-part and a domain separated by an (unquoted) “@” symbol. The local-part must be either a dot-atom or a quoted string, and the domain must be either a domain name or a domain literal.

A dot-atom can only contain letters, numbers, dots, and the following characters: ! # $ % & ‘ * + – / = ? ^ _ ` { | } ~. However, neither the first nor the last character can be a dot, and two or more consecutive dots are not allowed. The maximum length of a dot-atom is 64 characters. A regular expression to match for a dot-atom local-part would be as follows:

// Dot-atom

/^(?!.{65,})([!#-'*+\/-9=?^-~-]+)(?>\.(?1))*$/iD

Read More

Tags: ,

Sunday, December 20th, 2009 PHP 18 Comments