Zend_Log_Writer_Firebug gotcha

Posted by hobodave, Mon Oct 27 11:42:00 UTC 2008

A few weeks ago I found a gem of a class called Zend_Log_Writer_Firebug inside Zend Framework. It really is a must have when it comes to debug logging.

However, a few weeks ago something bizarre happened that caused all of my FirePHP logging to break. I scoured the web for bug reports to find issues similar but not quite the same. I wasn’t using AJAX calls, I could see the FirePHP headers in the response headers of FireBug, but I couldn’t see the formatted output.

I finally got fed up today. I hate var_dump’s and wanted my FirePHP logging back. After a near flying knee to my computer screen, I figured it out.

It appears that I was using FirePHP v0.1.2, and the latest was 0.2. For reasons beyond my knowledge, Firefox wasn’t detecting this as needing an upgrade.

After upgrading to 0.2 my FirePHP log messages worked again! I hope this shaves a few days of someone’s frustrations.

0 comments | Filed Under: PHP | Tags:

Updated hobo_validator

Posted by hobodave, Mon Jul 28 22:45:00 UTC 2008

Update: I found a bug with the way exceptions are handled. As before, the latest version can be found on the hobo_validator github. I also went ahead and set this project up on Lighthouse. All bugs/feature requests should be posted at the hobo_validator lighthouse to ensure quickest response.

0 comments | Filed Under: PHP | Tags:

Model validations with Zend & Doctrine

Posted by hobodave, Thu Jul 17 14:15:00 UTC 2008

In working with with Doctrine I found the built-in validations (Doctrine_Validator) to be sorely lacking. Specifically, there is no capability to define custom error messages. The doctrine validators work on the concept of ‘error codes’ which are totally vague, and not very useful.

Meanwhile Zend Framework does it’s validations in something called Zend_Form. I don’t currently use Zend_Form, and personally don’t believe validations belong in a ‘Form’ object, but in the model itself.

Thus I rolled together something I dub Hobo_Validator. Please help yourself to my implementation, and modify it as you see fit. You can find it hosted at my github. I suggest cloning the git tree via:

git clone git://github.com/hobodave/hobo_validator.git

To use, first enable the validation in a manner similar to the following (you will need to use your own condition for the if statement):


// Use your OWN conditional :)
if(dConfig::get('server_validation')) {
    Zend_Registry::set(Hobo::ATTR_VALIDATE, Hobo::VALIDATE_ALL);
} else {
    Zend_Registry::set(Hobo::ATTR_VALIDATE, Hobo::VALIDATE_NONE);
}

Then, you simply add the required validations in the construct() ( NOT: __construct ) method of your Hobo_Record models. e.g.


public function construct() {
    $this->_validator = new Hobo_Validator($this);
    $this->addValidation(explode(' ', 'requestor company_id requestor_email requestor_phone'), 'notBlank');
    $this->addValidation('age', 'greaterThan', array('params' => array(21));

The default validators provided are listed below, as well as their associated default error messages.


/**
 * Default error messages.
 * 
 *
 * @var array
 **/
protected static $_defaultErrorMessages = array(
    'inclusion'    => 'is not included in the list',
    'exclusion'    => 'is reserved',
    'invalid'      => 'is invalid',
    'notEmpty'     => "can't be empty",
    'notBlank'     => "can't be blank",
    'tooLong'      => "is too long (maximum is %d characters)",
    'tooShort'     => "is too short (minimum is %d characters)",
    'length'       => "is the wrong length (should be %d characters)",
    'notTaken'     => 'has already been taken',
    'notNumber'    => 'is not a number',
    'notOdd'       => 'must be odd',
    'notEven'      => 'must be even',
    'greaterThan'  => "must be greater than %d",
    'lessThan'     => "must be less than %d",
    'greaterThanOrEqualTo' => "must be greater than or equal to %d",
    'lessThanOrEqualTo'    => "must be less than or equal to %d" 
    );

The intent and scope of the Hobo_Validator is to provide a set of ‘core’ validations, those that are mostly likely to be used, as well as a method to roll your own.

To write your own custom validations, simply override the validate(), validateOnInsert(), or validateOnUpdate() methods in your models. For example, in my Files model (stores files in db) I use the following custom validate() method. This particular validation was copied/inspired by Rob Allan in his Zend_Form File Upload Example.


/**
 *
 * @return void
 * @author David Abdemoulaie
 **/
public function validate()
{
    $value = $this->_file;
    $error = UPLOAD_ERR_NO_FILE;

    if (is_array($value) && array_key_exists('error', $value)) {
        $error = $value['error'];
    }

    $result = false;
    switch ($error) {
        case UPLOAD_ERR_OK:
            $result = true;
            break;

        case UPLOAD_ERR_NO_FILE:
            $this->getErrors()->addToBase(self::$_errorMessages[self::NO_FILE]);
            break;

        case UPLOAD_ERR_INI_SIZE:
            $this->getErrors()->addToBase(self::$_errorMessages[self::INI_SIZE]);
            break;

        case UPLOAD_ERR_FORM_SIZE:
            $this->getErrors()->addToBase(self::$_errorMessages[self::FORM_SIZE]);
            break;

        case UPLOAD_ERR_PARTIAL:
            $this->getErrors()->addToBase(self::$_errorMessages[self::PARTIAL]);
            break;

        case UPLOAD_ERR_NO_TMP_DIR:
            $this->getErrors()->addToBase(self::$_errorMessages[self::NO_TMP_DIR]);
            break;

        case UPLOAD_ERR_CANT_WRITE:
            $this->getErrors()->addToBase(self::$_errorMessages[self::CANT_WRITE]);
            break;

        case UPLOAD_ERR_EXTENSION:
            $this->getErrors()->addToBase(self::$_errorMessages[self::EXTENSION]);
            break;

        default:
            $this->getErrors()->addToBase(self::$_errorMessages[self::ERROR]);
            break;
    }
    return $result;
}

I’ll be updating this post periodically with further examples, as well as providing additional examples in the README.

1 comment | Filed Under: PHP | Tags:

PhpGhettoDoc - Document your PHP w/ ruby?

Posted by hobodave, Mon Jun 23 14:37:00 UTC 2008

In a moment of boredom I decided I needed a simple way to document a giant batch of PHP files with skeleton PhpDocblocks. PHP not being my first language, I decided why not do it in ruby. I find ruby scripting to be quick, fast enough, and it has lots of great easy to use modules (e.g., Find).

That said here she is:


#!/usr/bin/env ruby
require 'ftools'
require 'optparse'
require 'find'

class PhpGhettoDoc

  Class_Doc = <<-EOF

/**
 * Undocumented class.
 *
 * @todo document me
 * @package unknown
 * @author unknown
 **/
EOF

  Function_Doc = <<-EOF

/**
 * Undocumented function.
 *
 * @todo document me
 * @return void
 * @author unknown
 **/
EOF

  Const_Doc = <<-EOF

/**
 * Undocumented constant.
 * @todo document me
 **/
EOF

  Var_Doc = <<-EOF

/**
 * Undocumented variable
 * @todo document me
 **/
EOF

  def initialize(file, backup = true)
    @backup = backup
    @word_matcher = /^(\s*)(class|const|function|public|interface|final|protected|private|static)+\s+([$\w]+)[\t ]*(\w+)*/
    @doc_matcher = /^\s*(\*|\/)+/

    @doc_blocks = {:class    => Class_Doc,
                   :function => Function_Doc,
                   :const    => Const_Doc,
                   :abstract  => Class_Doc,
                   :final     => Class_Doc,
                   :interface => Class_Doc,
                   :var       => Var_Doc}

    @file = file
    @dirty_lines = File.open(file, 'r+').readlines
    @modified = false
  end

  def parse_lines
    @clean_lines = Array.new
    @dirty_lines.each do |line|
      if (line.match @word_matcher)
        indent = $1
        keywords = ($4) ? Array[$2.intern, $3.intern, $4.intern] : Array[$2.intern, $3.intern]
        case $2.intern
          when :public, :protected, :private, :static
            type = (keywords.include? :function) ? :function : :var
          else 
            type = $2.intern
        end
        unless (@clean_lines.last =~ @doc_matcher || !type)
          doc_block = @doc_blocks[type]
          @clean_lines.push(doc_block.split("\n").map! { |l| l = indent + l }.join("\n") + "\n");
          @modified = true
        end
      end
      @clean_lines.push line
    end
  end

  def dump
    @clean_lines.each do |line|
      puts line
    end
  end

  def save
    if @modified
      if @backup
        File.move(@file, @file + '.bak.' + Time.now.strftime("%Y%m%d%H%M%S"))
      end
      f = File.new(@file, "w")
      @clean_lines.each { |line| f.write(line) }
      f.close
    end
    @modified
  end
end

if $0 == __FILE__
  options = { :save => false, :backup => false, :recurse => false }
  usage = "usage: #{__FILE__} [options] <file|directory>" 
  OptionParser.new do |opts|
    opts.banner = usage
    opts.on('-s', '--save', "Saves the parsed file to disk. (Default: false)") do |s|
      options[:save] = s
    end
    opts.on('-b', '--[no-]backup', 'Create backups. (Default: false)') do |b|
      options[:backup] = b
    end
    opts.on('-r', '--recurse', 'Recurse subdirectories. (Default: false)') do |r|
      options[:recurse] = r
    end
  end.parse!

  unless ARGV.length >= 1
    puts usage
    exit
  end

  modified = 0
  files = Array.new
  if File.directory?(ARGV[0])
    Find.find(ARGV[0]) do |path|
      if (FileTest.directory?(path))
        if (File.basename(path)[0] == ?. && path != '.')
          Find.prune    # Skip .foo directories
        elsif (! options[:recurse] && ARGV[0] != path)
          Find.prune    # Skip recurse
        else
          next
        end
      else
        if path =~ /.php$/
          files.push path
        end
      end
    end
  else
    files.push ARGV[0]
  end

  files.each do |file|
    p = PhpGhettoDoc.new(file, options[:backup])
    p.parse_lines
    if options[:save]
      modified += (p.save) ? 1 : 0
    else
      p.dump
    end    
  end

  puts "Processed #{files.size} files" 
  puts "Modified #{modified} files" 
end

0 comments | Filed Under: PHP | Tags:

First Post

Posted by hobodave, Thu Jun 05 03:52:00 UTC 2008

Mephisto is up and running!

0 comments | Filed Under: | Tags: