YView.class.php 15.3 KB
<?php/**
 * 路由处理
 * 
 * @name Framework_YView
 * @package framework
 * @version 0.2 (2012-12-14 17:14:29) <fei.hong@yoho.cn>
 * @since 0.1 (2012-6-25) xiaoma
 */class Framework_YView {
    /**
     * 视图文件根目录
     *
     * @var string
     */
    protected $_basePath = '';
    
    /**
     * 视图脚本存放目录
     *
     * @var string
     */
    protected $_scriptPath = '';

    /**
     * 当前使用的分析器
     *
     * @var object
     */
    protected $_viewParser;
    
    /**
     * 视图
     *
     * @var string
     */
    protected $_viewname = '';
    
    /**
     * 模板变量
     *
     * @var array
     */
    protected $_view = array();

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->cleanVars();
    }

    /**
     * 设置视图名称
     *
     * @param string $viewname
     * @return Framework_YView
     */
    public function setViewname($viewname)
    {
        $this->_viewname = $viewname;
        
        return $this;
    }
    
    /**
     * 设置视图根目录
     * 
     * @param string $path (视图根目录)
     * @return Framework_YView
     */
    public function setBasePath($path)
    {
    	$this->_basePath = $path;
    	
    	return $this;
    }
    
    /**
     * 设置视图脚本目录 
     * 
     * @param string $path (视图脚本目录 )
     * @return Framework_YView
     */
    public function setScriptPath($path)
    {
    	$this->_scriptPath = $path;
    	
    	return $this;
    }
    
    /**
     * 设置视图的解析器
     * 
     * @param Framework_YView_Parser $parser (视图解析器)
     * @return Framework_YView
     */
    public function setViewParser(Framework_YView_Parser $parser)
    {
    	$this->_viewParser = new $parser();
    	
    	return $this;
    }

    /**
     * 指定模板变量
     *
     * @param string | array $key
     * @param mixed $value
     * @return Framework_YView
     */
    public function assign($key, $value = null)
    {
        if (is_array($key))
        {
            $this->_view = array_merge($this->_view, $key);
        }
        else
        {
            $this->_view[$key] = $value;
        }
        
        return $this;
    }

	/**
     * 清除所有模板变量
     *
     * @return Framework_YView
	 */
	public function cleanVars()
    {
        $context = Framework_YHttpRequest::instance();
        $this->_vars = array(
            '_ctx'          => $context,
            '_BASE_DIR'     => $context->baseDir(),
            '_BASE_URI'     => $context->baseUri(),
            '_REQUEST_URI'  => $context->requestUri(),
        );
        return $this;
    }
    
    /**
     * 部分渲染视图 
     *  
     * (直接包含方式, 不解析页面)
     * 
     * @param string $viewname (视图名称)
     * @param mixed $params (向视图传递的参数)
     * @return string
     * @since 0.2
     */
    public function renderPartial($viewname = null, $params = null)
    {
    	if (null !== $viewname)
    	{
	    	$viewname = $this->_basePath . $this->_scriptPath . DS . $viewname . '.php';
	    	if (file_exists($viewname))
	    	{
	    		if (is_array($params))
	    		{
	    			extract($params);
	    		}
				return include($viewname);
	    	}
    	}
    	return '';
    }

    /**
     * 渲染视图并返回渲染结果
     * 
     * @param string $viewname
     * @param array $params
     * @return string
     * @since 0.2
     */
    public function render($viewname = null, $params = null)
    {
    	// 渲染之前执行
    	$this->beforeRender();
    	
		if (empty($viewname))
		{
			$viewname = $this->_viewname;
		}
		$viewname = $this->_basePath . $this->_scriptPath . DS . $viewname . '.php';
		if (file_exists($viewname))
		{
			if (null === $params)
			{
				$params = $this->_view;
			}
			// 设置默认的视图解析器
			if (is_null($this->_viewParser) || ! is_object($this->_viewParser))
			{
				$this->_viewParser = new Framework_YView_Parser($this->_basePath, $this->_scriptPath);
			}
			// 解析视图页面, 返回视图页面内容
			$output = $this->_viewParser->assign($params)->parse($viewname);
		}
		else
		{
			$output = '';
		}
		
		// 渲染之后执行
		$this->afterRender($output);
		
		return $output;
    }

    /**
     * 渲染之前调用
     *
     * 继承类可以覆盖此方法。
     */
    protected function beforeRender() {}

    /**
     * 渲染之后调用
     *
     * 继承类可以覆盖此方法。
     *
     * @param string $output
     */
    protected function afterRender(& $output) {}
    
}

/**
 * 视图变量对象
 * 
 * @version  0.1 2012-7-24
 * @author xiaoma
 * @name Framework_YView_Var
 */
class Framework_YView_Var
{
    /**
     * 当前对象
     *
     * @var Framework_YView_Var
     */
    private static $instance = null ;
    
    /**
     * 返回对象
     *
     * @return Framework_YView_Var
     */
    public static function getInstance ()
    {
        if (is_null(self::$instance))
        {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * 设置视图变量
     *
     * @param string $name
     * @param mixed $value
     */
    public function __set($name,$value)
    {
        $this->$name = $value;
    }
    
    /**
     * 未定义的属性  返回null 
     *
     * @param string $name
     * @return null
     */
    public function __get($name)
    {
        return null;
    }
}

/**
 * Framework_YView_Parser 类实现了视图的分析
 *
 * @package mvc
 */
class Framework_YView_Parser
{
    /**
     * 视图文件扩展名
     * 
     * @var string
     */
    protected $_extname = 'php';

    /**
     * 视图堆栈
     *
     * @var array
     */
    private $_stacks = array();

    /**
     * 当前处理的视图
     *
     * @var int
     */
    private $_current;

    /**
     * 视图变量
     *
     * @var array
     */
    protected $_vars;

    /**
     * 视图文件所在目录
     *
     * @var string
     */
    private $_viewBasePath;
    
    /**
     * 视图脚本文件所在目录
     *
     * @var string
     */
    private $_viewScriptPath;

    /**
     * 变量对象
     *
     * @var Framework_YView_Var
     */
    public $view;
    /**
     * 构造函数
     */
    public function __construct($viewBasePath, $viewScriptPath)
    {
        $this->_viewBasePath = $viewBasePath;
        $this->_viewScriptPath = $viewScriptPath;
        
        $this->view = Framework_YView_Var::getInstance();
    }

    /**
     * 设置分析器已经指定的变量
     *
     * @param array $vars
     *
     * @return Framework_YView_Parser
     */
    public function assign(array $vars)
    {
        $this->_vars = $vars;
        //将数组转换成对象 方便视图使用
        foreach ($vars as $key => $val)
        {
//             if(is_string($val)&&!isset($vars['no_format']))
//             {
//             	$this->view->$key= strtr($val, array('<'=> '&lt;', '>'=>'&gt',));
//             }
//             else 
//             {
            	$this->view->$key= $val ;
            //}
        }
        return $this;
    }

    /**
     * 返回视图文件的扩展名
     *
     * @return string
     */
    public function getExtName()
    {
        return $this->_extname;
    }
    
    /**
     * 设置视图文件扩展名
     * 
     * @param string $ext
     * @since 0.2
     */
    public function setExtName($ext)
    {
    	$this->_extname = $ext;
    }

    /**
     * 分析一个视图文件并返回结果
     *
     * @param string $filename
     * @param string $view_id
     * @param array $inherited_stack
     *
     * @return string
     */
    public function parse($filename, $view_id = null, array $inherited_stack = null)
    {
        if (! $view_id)
        { 
        	$view_id = mt_rand();
        }

        $stack = array(
            'id'            => $view_id,
            'contents'      => '',
            'extends'       => '',
            'blocks_stacks' => array(),
            'blocks'        => array(),
            'blocks_config' => array(),
            'nested_blocks' => array(),
        );
        array_push($this->_stacks, $stack);
        
        $this->_current = count($this->_stacks) - 1;
        
        unset($stack);

        ob_start();
        
        $this->_include($filename);
        
        $stack = $this->_stacks[$this->_current];
        $stack['contents'] = ob_get_clean();

        // 如果有继承视图,则用继承视图中定义的块内容替换当前视图的块内容
        if (is_array($inherited_stack))
        {
            foreach ($inherited_stack['blocks'] as $block_name => $contents)
            {
                if (isset($stack['blocks_config'][$block_name]))
                {
                    switch (strtolower($stack['blocks_config'][$block_name]))
                    {
	                    case 'append':
	                        $stack['blocks'][$block_name] .= $contents;
	                        break;
	                    case 'replace':
	                    default:
	                        $stack['blocks'][$block_name] = $contents;
                    }
                }
                else
                {
                    $stack['blocks'][$block_name] = $contents;
                }
            }
        }

        // 如果有嵌套 block,则替换内容
        while (list($child, $parent) = array_pop($stack['nested_blocks']))
        {
            $stack['blocks'][$parent] = str_replace("%block_contents_placeholder_{$child}_{$view_id}%",
                $stack['blocks'][$child], $stack['blocks'][$parent]
            );
            unset($stack['blocks'][$child]);
        }

        // 保存对当前视图堆栈的修改
        $this->_stacks[$this->_current] = $stack;

        if ($stack['extends'])
        {
            // 如果有当前视图是从某个视图继承的,则载入继承视图
            $filename = "{$this->_viewBasePath}/{$stack['extends']}.$this->_extname";
            
            return $this->parse($filename, $view_id, $this->_stacks[$this->_current]);
        }
        else
        {
            // 最后一个视图一定是没有 extends 的
            $last = array_pop($this->_stacks);
            foreach ($last['blocks'] as $block_name => $contents)
            {
                $last['contents'] = str_replace("%block_contents_placeholder_{$block_name}_{$last['id']}%",
                    $contents, $last['contents']
                );
            }
            $this->_stacks = array();

            return $last['contents'];
        }
    }

    /**
     * 视图的继承
     *
     * @param string $tplname
     *
     * @access public
     */
    protected function _extends($tplname)
    {
        $this->_stacks[$this->_current]['extends'] = $tplname;
    }

    /**
     * 开始定义一个区块
     *
     * @param string $block_name
     * @param mixed $config
     *
     * @access public
     */
    protected function _block($block_name, $config = null)
    {
        $stack = & $this->_stacks[$this->_current];
        if (!empty($stack['blocks_stacks']))
        {
            // 如果存在嵌套的 block,则需要记录下嵌套的关系
            $last = $stack['blocks_stacks'][count($stack['blocks_stacks']) - 1];
            $stack['nested_blocks'][] = array($block_name, $last);
        }
        $this->_stacks[$this->_current]['blocks_config'][$block_name] = $config;
        array_push($stack['blocks_stacks'], $block_name);
        ob_start();
    }

    /**
     * 结束一个区块
     *
     * @access public
     */
    protected function _endblock()
    {
        $block_name = array_pop($this->_stacks[$this->_current]['blocks_stacks']);
        $this->_stacks[$this->_current]['blocks'][$block_name] = ob_get_clean();
        echo "%block_contents_placeholder_{$block_name}_{$this->_stacks[$this->_current]['id']}%";
    }

    /**
     * 构造一个组件
     *
	 * @param string $alias (组件路径)
	 * @param array $params (组件参数)
     * @access public
     */
    protected function _component($alias, $params = array(), $id = '')
    {
    	$component = new Framework_YComponent($id);
    	$component->execute($alias, $params);
    }

    /**
     * 载入一个视图片段
     *
     * @param string $element_name
     * @param array $vars
     *
     * @access public
     */
    protected function _widget($element_name, $vars = null)
    {
        $element_name = str_replace ( '_', DIRECTORY_SEPARATOR, $element_name );
        $filename = "{$this->_viewBasePath}/widget/{$element_name}.{$this->_extname}";
        $this->_include($filename, $vars);
    }

    /**
     * 载入视图文件
     */
    protected function _include($filename, $vars = null)
    {
        $this->_extname = pathinfo($filename, PATHINFO_EXTENSION);
        if (is_array($vars)) 
        {
        	extract($vars);
        }
        include $filename;
    }
    
    /**
     * 引入CSS文件方法
     *
     * @param unknown_type $group_name
     * @param array $styles
     */
    protected function _css($group_name='',array $styles = null,$noEchoInGroup=false,$noload=false)
    {
    	$minFile = C('APP.MinFile');
    	
    	if ($minFile['open'] && $group_name != ''&&$this->_isCombinePage($group_name)&&!isset($_GET['debug'])) 
    	{
    	    if(!$noEchoInGroup)
    		{
    		    echo  '<link href="'.$minFile['css'].$group_name.'&site='.$minFile["site"].'&v='.$minFile["version"].'" media="screen" rel="stylesheet" type="text/css" />', PHP_EOL;
    		}
    	}
    	else
    	{
    	    if(!$noload)
    	    {
        	    foreach ($styles as $style)
        		{
        			echo '<link href="'.$style.'" media="screen" rel="stylesheet" type="text/css" />', PHP_EOL;
        		}
    	    }
    	} 
    	// 压缩组合js,css配置自动生成
    	if (!$minFile['open']&&isset($_GET['gengroup']))
    	{
    	    echo '<script type="text/javascript">var groupname_css = "'.$group_name.'"</script>', PHP_EOL;
    	}
    }
    
    
    /**
     * 引入js文件方法
     *
     * @param unknown_type $group_name
     * @param array $js
     */
    protected function _js($group_name='',array $js = null,$noEchoInGroup=false,$noload=false)
    {
    	$minFile = C('APP.MinFile');

    	if ($minFile['open'] && $group_name != ''&&$this->_isCombinePage($group_name)&&!isset($_GET['debug'])) 
    	{
    	    if(!$noEchoInGroup)
    	    {
    	      echo '<script type="text/javascript" src="'.$minFile['js'].$group_name.'&site='.$minFile["site"].'&v='.$minFile["version"].'"></script>', PHP_EOL;
    	    }
    	}
    	else
    	{
    		if(!$noload)
    		{
    		    foreach ($js as $s)
    		    {
    		        echo '<script type="text/javascript" src="',$s,'"></script>', PHP_EOL;
    		    }
    		}
    	}
    	//压缩组合js,css配置自动生成
    	if (!$minFile['open']&&isset($_GET['gengroup']))
    	{
    	    echo '<script type="text/javascript">var groupname_js = "'.$group_name.'"</script>', PHP_EOL;
    	    echo '<script type="text/javascript" src="'.SITE_JS.'/group.js"></script>', PHP_EOL;
    	}
    }
    
    private function _isCombinePage($group_name)
    {
        $combinePage = C('APP.combinePage');
        $isCombinePage = false;
        foreach($combinePage as $val)
        {
            if($val==$group_name)
            {
                $isCombinePage = true;
            }
        };
        return $isCombinePage;
    }
    }