Location: PHPKode > projects > SolarPHP > solar-system-1.1.1/solar/source/solar/Solar/Uri/Rewrite.php
<?php
/**
 * 
 * Rewrites URI action paths, and creates URI paths from the rewrite rules.
 * 
 * @category Solar
 * 
 * @package Solar_Uri Representation and manipulation of URIs (generic, 
 * action, and public).
 * 
 * @author Paul M. Jones <hide@address.com>
 * 
 * @license http://opensource.org/licenses/bsd-license.php BSD
 * 
 * @version $Id: Rewrite.php 4506 2010-03-08 22:37:19Z pmjones $
 * 
 */
class Solar_Uri_Rewrite extends Solar_Base
{
    /**
     * 
     * Default configuration values.
     * 
     * @config array rewrite An array of rewrite rules.
     * 
     * @config array replace An array of regex replacement tokens.
     * 
     * @var array
     * 
     */
    protected $_Solar_Uri_Rewrite = array(
        'rewrite' => array(),
        'replace' => array(),
    );
    
    /**
     * 
     * The collection of rewrite rules.
     * 
     * @var array
     * 
     */
    protected $_rewrite = array();
    
    /**
     * 
     * The collection of replacement regex tokens.
     * 
     * @var array
     * 
     */
    protected $_replace = array(
        '{:action}'     => '([a-z-]+)',
        '{:alpha}'      => '([a-zA-Z]+)',
        '{:alnum}'      => '([a-zA-Z0-9]+)',
        '{:controller}' => '([a-z-]+)',
        '{:digit}'      => '([0-9]+)',
        '{:param}'      => '([^/]+)',
        '{:params}'     => '(.*)',
        '{:slug}'       => '([a-zA-Z0-9-]+)',
        '{:word}'       => '([a-zA-Z0-9_]+)',
    );
    
    /**
     * 
     * The default rewrite rule array.
     * 
     * The `pattern` key is the incoming URI regex pattern to match against.
     * 
     * The `rewrite` key is the canonical "controller/action/param" format
     * the incoming URI should be rewritten to.
     * 
     * The `replace` key is an array of custom replacement regex tokens
     * for this particular rewrite rule.
     * 
     * The `default` key is an array of default values for tokens when data
     * for interpolation is missing.
     * 
     * @var array
     * 
     */
    protected $_default = array(
        'pattern' => null,
        'rewrite' => null,
        'replace' => array(),
        'default' => array(),
    );
    
    /**
     * 
     * An explanation of the last match() attempt.
     * 
     * @var string
     * 
     */
    protected $_explain;
    
    /**
     * 
     * Post-construction tasks to complete object construction.
     * 
     * @return void
     * 
     */
    protected function _postConstruct()
    {
        if ($this->_config['rewrite']) {
            $this->_rewrite = $this->_config['rewrite'];
        }
        
        if ($this->_config['replace']) {
            $this->_replace = $this->_config['replace'] + $this->_replace;
        }
    }
    
    /**
     * 
     * Sets one rewrite rule; adds it if it is not there, or changes it if it
     * is.
     * 
     * {{code: php
     *     // shorthand form is 'pattern' and 'rewrite'.
     *     $rewrite->setRule('blog/{:id}/edit', 'blog/edit/$1')
     *     
     *     // longhand form is 'rule-name', and an array of information.
     *     // this is what lets you generate named actions using getPath().
     *     $rewrite->setRule('blog-edit', array(
     *         // the pattern to match
     *         'pattern' => 'blog/{:id}/edit',
     *         // rewrite to this
     *         'rewrite' => 'blog/edit/$1',
     *         // custom replacement tokens just for this rule
     *         'replace' => array(
     *             '{:id}' => '(\d*)
     *         ),
     *     ));
     * }}
     * 
     * @param string $key A shorthand pattern, or a longhand rule name.
     * 
     * @param mixed $val A shorthand rewrite string, or a longhand array
     * of information.
     * 
     * @return void
     * 
     */
    public function setRule($key, $val)
    {
        $this->_rewrite[$key] = $val;
    }
    
    /**
     * 
     * Gets a rewrite rule by its key; the key may be a pattern, or a named
     * rule.
     * 
     * @param string $key The rule key.
     * 
     * @return mixed The shorthand rewrite string, or the longhand information
     * array.
     * 
     */
    public function getRule($key)
    {
        if (! empty($this->_rewrite[$key])) {
            return $this->_rewrite[$key];
        }
    }
    
    /**
     * 
     * Gets all the rewrite rules.
     * 
     * @return array
     * 
     */
    public function getRules()
    {
        return $this->_rewrite;
    }
    
    /**
     * 
     * Reset all the rewrite rules.
     * 
     * @param array $list Use these rules instead of the existing ones.
     * 
     * @return void
     * 
     */
    public function resetRules($list = null)
    {
        $this->_rewrite = (array) $list;
    }
    
    /**
     * 
     * Merges a new list of rules with the existing ones; the new ones
     * will replace any existing rules with the same keys.
     * 
     * @param array $list Merge these rules with the existing ones.
     * 
     * @return void
     * 
     */
    public function mergeRules($list)
    {
        $this->_rewrite = (array) $list + $this->_rewrite;
    }
    
    /**
     * 
     * Sets one replacement regex token.
     * 
     * @param string $key The token name.
     * 
     * @param string $val The regex replacement.
     * 
     * @return void
     * 
     */
    public function setReplacement($key, $val)
    {
        $this->_replace[$key] = $val;
    }
    
    /**
     * 
     * Gets the replacement regex for a token.
     * 
     * @param string $key The token name.
     * 
     * @return string The replacement regex.
     * 
     */
    public function getReplacement($key)
    {
        if (! empty($this->_replace[$key])) {
            return $this->_replace[$key];
        }
    }
    
    /**
     * 
     * Gets all replacement regex tokens.
     * 
     * @return array The replacement regex tokens.
     * 
     */
    public function getReplacements()
    {
        return $this->_replace;
    }
    
    /**
     * 
     * Reset all replacement regex tokens.
     * 
     * @param array $list Use these tokens instead of the existing ones.
     * 
     * @return void
     * 
     */
    public function resetReplacements($list = null)
    {
        $this->_replace = (array) $list;
    }
    
    /**
     * 
     * Merges a new list of replacement regex tokens with the existing ones; 
     * the new ones will replace any existing replacements with the same keys.
     * 
     * @param array $list Merge these tokens with the existing ones.
     * 
     * @return void
     * 
     */
    public function mergeReplacements($list = null)
    {
        $this->_replace = (array) $list + $this->_replace;
    }
    
    /**
     * 
     * Given a URI path, matches it against a rewrite rule and returns the
     * rewritten path.
     * 
     * @param string|Solar_Uri $spec The original URI path.
     * 
     * @return string The rewritten path.
     * 
     */
    public function match($spec)
    {
        $this->_explain = null;
        
        // pre-empt if no rules
        if (! $this->_rewrite) {
            $this->_explain = 'no rules';
            return;
        }
        
        // convert spec to a path
        if ($spec instanceof Solar_Uri_Action) {
            $oldpath = trim($spec->getFrontPath(), '/');
        } elseif ($spec instanceof Solar_Uri) {
            $oldpath = trim($spec->getPath());
        } else {
            $oldpath = $spec;
        }
        
        // go through each of the rules to find a match
        foreach ($this->_rewrite as $name => $info) {
            
            if (! is_array($info)) {
                // shorthand format
                $info = array(
                    'pattern' => $name,
                    'rewrite' => $info,
                    'replace' => array(),
                    'default' => array(),
                );
            } else {
                // longhand format
                $info += $this->_default;
            }
            
            // consolidate replacements
            $replace = $info['replace'] + $this->_replace;
            
            // convert replacements in the pattern
            $pattern = str_replace(
                array_keys($replace),
                array_values($replace),
                $info['pattern']
            );
            
            // trim slashes and wrap as a full regex
            $pattern = '#^' . trim($pattern, '/') . '$#';
            
            // is it a match?
            if (preg_match($pattern, $oldpath)) {
                // rewrite to new path and trim slashes
                $rewrite = trim($info['rewrite'], '/');
                $newpath = preg_replace($pattern, $rewrite, $oldpath);
                $this->_explain = "matched rule '$name'";
                return trim($newpath, '/');
            }
        }
        
        $this->_explain = 'no matches';
    }
    
    /**
     * 
     * Explains the result of the last match() attempt.
     * 
     * @return string
     * 
     */
    public function explain()
    {
        return $this->_explain;
    }
    
    /**
     * 
     * Look up a named rewrite rule, replace the regex token placeholders
     * in the pattern with data values, and return the resulting path.
     * 
     * @param string $name The rewrite rule name.
     * 
     * @param array $data Key-value pairs to use as replacements for the
     * regex token placeholders in the pattern.
     * 
     * @return string The named rewrite rule with data in it.
     * 
     */
    public function getPath($name, $data = null)
    {
        // is there a rule with this name?
        if (empty($this->_rewrite[$name])) {
            return false;
        }
        
        // look up the rule and get its info
        $info = $this->_rewrite[$name];
        if (! is_array($info)) {
            // not a named rule
            return false;
        }
        
        // the path with tokens still in it
        $path = $info['pattern'];
        
        // if there's no data, there's nothing to replace with
        if (! $data) {
            return $path;
        }
        
        // find all the tokens in the pattern
        preg_match_all('/\{:(.*?)\}/', $path, $matches);
        
        // if there are no tokens, there's nothing to replace
        if (empty($matches[1])) {
            return $path;
        }
        
        // interpolate data over the tokens
        foreach ($matches[1] as $key) {
            $token = "{:$key}";
            if (isset($data[$key])) {
                // use data value
                $path = str_replace($token, $data[$key], $path);
            } elseif (array_key_exists($token, $info['default'])) {
                // use default value
                $path = str_replace($token, $info['default'][$token], $path);
            }
        }
        
        // done!
        return $path;
    }
}
Return current item: SolarPHP