我想我还是回到Joomla!原本的组件的MVC设计中。
以JoomSport为例,它的模块入口为:/components/com_joomsport/joomsport.php。打开它来看看它都做了哪些事:
<?php
// no direct access
defined('_JEXEC') or die('Restricted access');
// Require specific controller if requested
if($controller = JRequest::getWord('controller'))
{
//var_dump($controller); //test no controller
$path = JPATH_COMPONENT.DS.'controllers'.DS.$controller.'.php';
if (file_exists($path) && $controller)
{
require_once $path;
}
else
{
$controller = '';
require_once(JPATH_COMPONENT.DS.'controller.php');
}
}
else
{
$controller = '';
require_once(JPATH_COMPONENT.DS.'controller.php');
}
// Create the controller
$classname = 'BLeagueController'.ucfirst($controller);
$controller = new $classname( );
$seasid = JRequest::getVar( 'seasid', 0, '', 'int' );
//var_dump($seasid); //test 0
$sid = JRequest::getVar( 'sid', 0, '', 'int' );
//var_dump($sid); //test 1
if($seasid && !$sid)
{
//won't reach here
JRequest::setVar( 'sid', $seasid );
}
// Perform the Request task
$task = JRequest::getWord('task');
//var_dump($task); //test empty str
$controller->execute(JRequest::getVar('task', null, 'default', 'cmd'));
// Redirect if set by the controller
$controller->redirect();
大致上可以将这个PHP文件的功能划分为四个部分:
首先,第一个部分:
// Require specific controller if requested
if($controller = JRequest::getWord('controller'))
{
//var_dump($controller); //test no controller
$path = JPATH_COMPONENT.DS.'controllers'.DS.$controller.'.php';
if (file_exists($path) && $controller)
{
require_once $path;
}
else
{
$controller = '';
require_once(JPATH_COMPONENT.DS.'controller.php');
}
}
else
{
$controller = '';
require_once(JPATH_COMPONENT.DS.'controller.php');
}
实际上,在JoomSport的设计中,C(Controller,控制)的部分只有一个单独的controller,所有的不同任务实际上都是交给了它来处理的,因为controller参数自身的作用在于告诉Joomla!要执行的controller,然而现在的情况是既然只有一个那么根本不需要区分,所以这个参数也就没有用处了,因而在JoomSport的实现里这个跟在URL后面的参数总是被忽略的,所以每次输出它的值都是一个空值。于是程序的执行跳进else的部分,即加载唯一的controller.php。
第二个部分:
// Create the controller
$classname = 'BLeagueController'.ucfirst($controller);
$controller = new $classname( );
既然已经确定了需要加载的controller的类名,并且也已经将它的文件导入现在的执行文件中,那么就可以实例化这个控制类了。
第三个部分:
$seasid = JRequest::getVar( 'seasid', 0, '', 'int' );
//var_dump($seasid); //test 0
$sid = JRequest::getVar( 'sid', 0, '', 'int' );
//var_dump($sid); //test 1
if($seasid && !$sid)
{
//won't reach here
JRequest::setVar( 'sid', $seasid );
}
我并不是很清楚这个部分在设计实现上的意图,不过单就代码来看,像是某种后备处理机制,而从实际运行的情况来看,if判断从来没有可能是ture的情况。
最后部分:
// Perform the Request task
$task = JRequest::getWord('task');
//var_dump($task); //test empty str
$controller->execute(JRequest::getVar('task', null, 'default', 'cmd'));
// Redirect if set by the controller
$controller->redirect();
一个controller可以执行若干个task,所以这部分在通过url参数中的task字段来确认要执行的task。然而在多数的实际测试中,task通常是空字符,即是缺省的,而在缺省情况下,一个controller的默认task是display()。
那么,现在我们再来看看这个唯一的controller里究竟有些什么task。
class BLeagueController extends JController
{
/**
* Method to display a view
*
* @access public
* @since 1.5
*/
function display()
{
//echo 'display'; //test go here by default
$view = JRequest::getCmd( 'view' );
//var_dump($view); //test 'ltable'
if(!$view)
{
$view = 'ltable';
}
JRequest::setVar( 'view', $view );
$cal = JRequest::getCmd( 'layout' );
//var_dump($cal); //test empty
if($cal == 'calendar')
{
JRequest::setVar( 'view', 'calendar' );
JRequest::setVar( 'layout', 'default' );
}
//echo $view;
parent::display();
}
function team()
{
JRequest::setVar( 'view', 'blteam' );
parent::display();
}
function view_match()
{
//echo 'view match task';
JRequest::setVar( 'view', 'match' );
parent::display();
}
function player()
{
JRequest::setVar( 'view', 'player' );
parent::display();
}
function calendar()
{
JRequest::setVar( 'view', 'calendar' );
parent::display();
}
function get_js_version()
{
$js_version = '1.1.3.3';
echo $js_version;
exit();
}
}
循着这个思路,既然display()总是默认的task,那么我们应该分析下这个方法所做的事情之后继续去看下这个controller拥有的view。可是我注意到最后的一个方法get_js_version(),它使用echo输出了一些字符之后,就用exit()来终止PHP脚本的执行了。那么这样会导致浏览器收到什么结果呢?我们不妨用这个链接来试一试:
http://localhost/soccer/index.php?option=com_joomsport&task=get_js_version
结果是浏览器真的只是收到一串字符:1.1.3.3。