<?php
/**
* Web Application Component Toolkit
*
* @link http://www.phpwact.org/
*
* @author Wact Development Team
* @link http://www.phpwact.org/team
*
* @copyright Copyright 2006, Jeff Moore
* @license http://opensource.org/licenses/mit-license.php MIT
*/

/**
*   Provides indirect path access to object and array values
*/
class Wact_Property_Binder {

    const DONT_CHECK_IF_EXISTS = 0;
    const NULL_IF_NOT_FOUND = 1;
    const EXCEPTION_IF_NOT_FOUND = 2;

    /**
    */
    static function get($obj, $name, $policy = self::DONT_CHECK_IF_EXISTS) {
        if (is_object($obj)) {
            if ($policy !== self::DONT_CHECK_IF_EXISTS) {
                if (!isset($obj->$name) && !property_exists($obj, $name)) {
                    if ($policy == self::NULL_IF_NOT_FOUND) {
                        return NULL;
                    } else {
                        throw new Wact_Property_Exception_PropertyNotFound($name);
                    }
                }
            }
            return $obj->$name;
        } else if (is_array($obj)) {
            if ($policy !== self::DONT_CHECK_IF_EXISTS) {
                if (!isset($obj[$name]) && !array_key_exists($name, $obj)) {
                    if ($policy == self::NULL_IF_NOT_FOUND) {
                        return NULL;
                    } else {
                        throw new Wact_Property_Exception_IndexNotFound($name);
                    }
                }
            }
            return $obj[$name];
        } else {
            throw new Wact_Property_Exception_Dereference($name);
        }
    }

    /**
    */
    static function set(&$obj, $name, $value) {
        if (is_object($obj)) {
            $obj->{$name} = $value;
        } else if (is_array($obj)) {
            $obj[$name] = $value;
        } else {
            throw new Wact_Property_Exception_Dereference($name);
        }
    }

    /**
    */
    static function exists($obj, $name) {
        if (is_object($obj)) {
            return isset($obj->$name) || property_exists($obj, $name);
        } else if (is_array($obj)) {
            return isset($obj[$name]) || array_key_exists($name, $obj);
        } else {
            throw new Wact_Property_Exception_Dereference($name);
        }
    }

    /**
    */
    static function getPath($obj, $path, $policy = self::DONT_CHECK_IF_EXISTS) {
        if (($pos = strpos($path, '.')) === FALSE) {
            return self::get($obj, $path, $policy);
        }

        $key = substr($path, 0, $pos);
        $rest = substr($path, $pos + 1);

        return self::getPath(self::get($obj, $key), $rest, $policy);
    }

    /**
    */
    static function existsPath($obj, $path) {
        if (($pos = strpos($path, '.')) === FALSE) {
            return self::exists($obj, $path);
        }

        $key = substr($path, 0, $pos);
        $rest = substr($path, $pos + 1);

        // inline this get call?
        $newobj = self::get($obj, $key);
        if ($newobj == NULL) {
            return FALSE;
        }
        return self::existsPath($newobj, $rest);
    }

    /**
    */
    static function createPath($path, $value) {
        $root = new StdClass();
        $obj = $root;
        $rest = $path;
        
        while (($pos = strpos($rest, '.')) !== FALSE) {
            $key = substr($rest, 0, $pos);
            $rest = substr($rest, $pos + 1);

            $obj->$key = new StdClass();
            $obj = $obj->$key;
        }
        $obj->$rest = $value;

        return $root;
    }

    /**
    */
    static function setPath(&$obj, $path, $value) {
    
        // This method has a serious flaw if __get or __set is implemented, but not __isset
    
        if (($pos = strpos($path, '.')) === FALSE) {
            self::set($obj, $path, $value);
            return;
        }

        $key = substr($path, 0, $pos);
        $rest = substr($path, $pos + 1);

        if (is_object($obj)) {
            if (isset($obj->$key) || property_exists($obj, $key)) {
                self::setPath($obj->$key, $rest, $value);
            } else {
                $obj->$key = self::createPath($rest, $value);
            }
        } else if (is_array($obj)) {
            if (isset($obj[$key]) || array_key_exists($key, $obj)) {
                self::setPath($obj[$key], $rest, $value);
            } else {
                $obj[$key] = self::createPath($rest, $value);
            }
        } else {
            throw new Wact_Property_Exception_Dereference($name);
        }

    }

}

?>