<?php
/*
 * Copyright (c) 2006 ArcStone Technologies, Inc. and Austin T. Smith
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

class Overloader
{
    const 
ARG_ARRAY    0;
    const 
ARG_BOOLEAN  1;
    const 
ARG_FLOAT    2;
    const 
ARG_INT      3;
    const 
ARG_NULL     4;
    const 
ARG_NUMERIC  5;
    const 
ARG_OBJECT   6;
    const 
ARG_SCALAR   7;
    const 
ARG_RESOURCE 8;
    const 
ARG_STRING   9;
        
    private 
$possibleMatches = array();
    private 
$argumentsSubmitted = array();
    private 
$argumentTypes = array();
    private 
$argumentCount 0;
    
    
/**
     * Build WonderOverloader Object with array of method arguments
     *
     * @param Array $args
     * @author Austin Smith
     */
    
public function __construct(Array $args)
    {
        
$this->argumentsSubmitted $args;
        
$this->argumentCount count($args);
    }
    
    
/**
     * Check against an argument template for a match
     * 
     * @param Array $types
     * @return mixed object of stdClass on success, false on failure 
     */
    
public function match(Array $types)
    {
        
$this->possibleMatches[] = &$types;
        
        if(
count($types) == 0)
        {
            return (
$this->argumentCount == 0);
        }
        
        if(
count($types) !== $this->argumentCount)
        {
            return 
false;        
        }
        
        
$return = new stdClass;
        
$i 0;
        foreach(
$types as $name => $type)
        {
            
$arg =& $this->argumentsSubmitted[$i];
            if(
$this->isSameType($arg,$type))
            {
                
$return->$name =& $arg;
            }
            else
            {
                return 
false;
            }
            
$i++;
        }
        return 
$return;
    }
    
    
/**
     * Throw relevant errors
     * 
     * @author Austin Smith
     */
    
public function error()
    {
        
$error "Improper arguments submitted. Possible arguments are: ";
        
$argTypes = array();
        foreach(
$this->possibleMatches as $match)
        {
            
$buffer "(";
            
$tBuffer = array();
            foreach(
$match as $name => $type)
            {
                if(
is_string($type))
                {
                    
$tBuffer[] = $name " => object of class $type";
                }
                else
                {
                    
$tBuffer[] = $name " => " $this->int2str[$type];
                }
            }
            
$buffer .= implode(", ",$tBuffer) . ")";
            
$argTypes[] = $buffer;
        }
        throw new 
Exception($error ' ' implode(', ',$argTypes));
    }
    
    
/**
     * Fastest check to see if an argument matches a given type
     * 
     * @param mixed $arg
     * @param int $type
     */
    
private function isSameType($arg,$type)
    {
        return
            (
                (
$type === self::ARG_ARRAY        &&    is_array    ($arg)) ||
                (
$type === self::ARG_BOOLEAN    &&    is_bool     ($arg)) ||
                (
$type === self::ARG_FLOAT        &&    is_float    ($arg)) ||
                (
$type === self::ARG_INT        &&    is_int      ($arg)) ||
                (
$type === self::ARG_NULL        &&    is_null     ($arg)) ||
                (
$type === self::ARG_NUMERIC    &&    is_numeric  ($arg)) ||
                (
$type === self::ARG_OBJECT        &&    is_object   ($arg)) ||
                (
$type === self::ARG_SCALAR        &&    is_scalar   ($arg)) ||
                (
$type === self::ARG_RESOURCE    &&    is_resource ($arg)) ||
                (
$type === self::ARG_STRING        &&    is_string    ($arg)) || 
                (
is_string($type)                &&  is_object    ($arg)  && ($arg instanceof $type))
            );
    }
    
    private 
$int2str = array(
        
=> 'array',
        
=> 'boolean',
        
=> 'float',
        
=> 'int',
        
=> 'null',
        
=> 'numeric',
        
=> 'object',
        
=> 'scalar',
        
=> 'resource',
        
=> 'string'
    
) ;
}

?>