I've merged in the changes by scharfie@gmail.com. For more information please see http://www.bluemargin.com/cake_custom_errors.zip. Olle, can you extract some information from the app/views/pages/home.thtml of that file?

The point is to provide custom error messages for Cake. As you will see, the quality of documentation is astounding.

git-svn-id: https://svn.cakephp.org/repo/trunk/cake@255 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
pies 2005-06-19 06:45:55 +00:00
parent 6fa1c1b3d4
commit 37e42105c6
9 changed files with 363 additions and 85 deletions

View file

@ -0,0 +1,22 @@
<h1>Missing action</h1>
<p class="error">You are seeing this error because the action <em><?=$this->missing_action;?></em>
is not defined in controller <em><?=Inflector::camelize($this->name);?></em>
</p>
<p>
<span class="notice"><strong>Notice:</strong> this error is being rendered by the <code>app/views/errors/missing_action.thtml</code>
view file, a user-customizable error page for handling invalid action dispatches.</span>
</p>
<p>
<strong>Error</strong>: Unable to execute action <em><?=$this->missing_action;?></em> on
<em><?=Inflector::camelize($this->name);?></em>
</p>
<h2>Controller dump:</h2>
<pre>
<? print_r($this); ?>
</pre>

View file

@ -0,0 +1,20 @@
<h1>Missing controller</h1>
<p class="error">You are seeing this error because controller <em><?=$this->missing_controller;?></em>
could not be found.
</p>
<p>
<span class="notice"><strong>Notice:</strong> this error is being rendered by the <code>app/views/errors/missing_controller.thtml</code>
view file, a user-customizable error page for handling invalid controller dispatches.</span>
</p>
<p>
<strong>Fatal</strong>: Unable to load controller <em><?=$this->missing_controller;?></em>
</p>
<h2>Controller dump:</h2>
<pre>
<? print_r($this); ?>
</pre>

View file

@ -0,0 +1,22 @@
<h1>Missing view</h1>
<p class="error">You are seeing this error because the view <em><?=$this->missing_view;?></em>
for action <em><?=$this->params['action'];?></em>
in controller <em><?=Inflector::camelize($this->name);?></em> could not be found.
</p>
<p>
<span class="notice"><strong>Notice:</strong> this error is being rendered by the <code>app/views/errors/missing_view.thtml</code>
view file, a user-customizable error page for handling missing/invalid views during rendering.</span>
</p>
<p>
<strong>Fatal</strong>: Unable to load view file <em><?=$this->missing_view;?></em> for
action <em><?=$this->params['controller'];?>::<?=$this->params['action'];?></em>
</p>
<h2>Controller dump:</h2>
<pre>
<? print_r($this); ?>
</pre>

View file

@ -7,10 +7,10 @@
</head>
<body>
<h1><?=$title_for_layout?></h1>
<?=$content_for_layout?>
<div id="container">
<div id="content">
<?=$content_for_layout?>
</div>
</div>
</body>
</html>

View file

@ -1,4 +1,4 @@
<h2>Cake works!</h2>
<h1>Cake works!</h1>
<p>Your installation of Cake is functional. Edit <code>/app/views/pages/home.thtml</code> to change the contents of this page.</p>
@ -8,13 +8,13 @@
<h2>Features</h2>
<p><ul>
<ul>
<li>compatibile with PHP4 and PHP5</li>
<li>supplies integrated <acronym title="Create, Read, Update, Delete">CRUD</acronym> for database and simplified querying so you shouldn't need to write SQL for basic operations (although <em>some</em> familiarity with SQL is strongly recommended)</li>
<li>request dispatcher with good-looking, custom URLs</li>
<li>fast, flexible templating (PHP syntax with helper methods)</li>
<li>works from a website subdirectory, with very little Apache configuration involved (requires <code>.htaccess</code> files and <code>mod_rewrite</code> to work; these are available on most web servers)</li>
</ul></p>
</ul>
<p>Cake is in its early infancy, but it works and I'm using it on a few projects. Currently the Dispatcher is working, the Model has <acronym title="Create, Read, Update, Delete">CRUD</acronym> functionality but with no joins between tables yet. The Controller is working and has most basic functions (including <code>render()</code> for templating).</p>
@ -22,6 +22,6 @@
<p><b>NEW! <?=$this->linkOut('My Amazon Wishlish','http://www.amazon.com/gp/registry/registry.html?id=NODP8QT6LFTO')?></b> for when you'll want to show your appreciation.</p>
<p><?=$this->linkOut('Cake PHP Google Group','http://groups-beta.google.com/group/cake-php')?> &middot; <?=$this->linkOut('Cake Wiki (temporary)','http://cake.bplusf.net/')?> &middot; <?=$this->linkOut('Cake TRAC (SVN repository, etc.)','https://developers.nextco.com/cake')?><a href=""></a></p>
<p><?=$this->linkOut('Cake PHP Google Group','http://groups-beta.google.com/group/cake-php')?> &middot; <?=$this->linkOut('Cake Wiki (temporary)','http://cake.bplusf.net/')?> &middot; <?=$this->linkOut('Cake TRAC (SVN repository, etc.)','https://developers.nextco.com/cake')?></p>
<p>See <?=$this->linkOut('Cake website','http://sputnik.pl/cake')?> for more information.</p>

View file

@ -143,6 +143,24 @@ class Controller extends Template
parent::__construct();
}
function missing_controller()
{
$this->autoRender = false;
$this->render('../errors/missing_controller');
}
function missing_action()
{
$this->autoRender = false;
$this->render('../errors/missing_action');
}
function missing_view()
{
$this->autoRender = false;
$this->render('../errors/missing_view');
}
/**
* Redirects to given $url, after turning off $this->autoRender.
*

View file

@ -35,6 +35,12 @@
* Dispatches the request, creating appropriate models and controllers.
*/
define('DISPATCH_NO_CONTROLLER', 'missing_controller');
define('DISPATCH_UNKNOWN_CONTROLLER', 'missing_controller');
define('DISPATCH_NO_ACTION', 'missing_action');
define('DISPATCH_UNKNOWN_ACTION', 'missing_action');
define('DISPATCHER_UNKNOWN_VIEW', 'missing_view');
uses('error_messages', 'object', 'router', 'controller');
/**
@ -44,7 +50,8 @@ uses('error_messages', 'object', 'router', 'controller');
* @subpackage cake.libs
* @since Cake v 0.2.9
*/
class Dispatcher extends Object {
class Dispatcher extends Object
{
/**
* Base URL
@ -61,78 +68,129 @@ class Dispatcher extends Object {
/**
* Constructor.
*
*/
function __construct () {
function __construct ()
{
$this->base = $this->baseUrl();
parent::__construct();
}
/**
* Enter description here...
*
* @param unknown_type $url
* @return unknown
*/
function dispatch($url)
{
$params = $this->parseParams($url);
$result = $this->invoke($url);
return $result === true? $params: array();
}
/**
* Enter description here...
*
* @param string $url
* @return unknown
*/
function dispatch ($url)
function invoke ($url)
{
global $_POST, $_GET, $_FILES, $_SESSION;
$params = $this->parseParams($url);
$params = $this->parseParams($url);
$missing_controller = false;
$missing_action = false;
$missing_view = false;
// die if no controller set
if (empty($params['controller']))
$this->errorNoController($url);
if (empty($params['controller']))
{
$missing_controller = true;
}
else
{
$ctrl_name = Inflector::camelize($params['controller']);
$ctrl_class = $ctrl_name.'Controller';
$ctrl_name = Inflector::camelize($params['controller']);
$ctrl_class = $ctrl_name.'Controller';
if (!loadController($ctrl_name) || !class_exists($ctrl_class))
{
$missing_controller = true;
}
}
/**
* Find out if the specified controller exists, and die if not.
*/
if (!loadController($ctrl_name) || !class_exists($ctrl_class))
$this->errorUnknownController($url, $ctrl_name);
if ($missing_controller)
{
$ctrl_class = 'AppController';
$controller = new $ctrl_class($this);
$params['action'] = 'missing_controller';
$controller->missing_controller = $params['controller'];
}
else
{
// create controller
$controller = new $ctrl_class($this);
}
$ctrl_methods = get_class_methods($ctrl_class);
$ctrl_vars = get_class_vars($ctrl_class);
/**
* If _no_action_is set, check if the default action, index() exists. If it doesn't, die.
*/
// if action is not set, and the default Controller::index() method doesn't exist
if (empty($params['action']))
{
if (in_array('index', $ctrl_methods))
if (method_exists($controller, 'index'))
{
$params['action'] = 'index';
}
else
{
$this->errorNoAction($url);
$missing_action = true;
}
}
/**
* Check if the specified action really exists.
*/
if (!in_array($params['action'], $ctrl_methods))
// if the requested action doesn't exist
if (!method_exists($controller, $params['action']))
{
$this->errorUnknownAction($url, $ctrl_class, $params['action']);
$missing_action = true;
}
$controller = new $ctrl_class ($params);
$controller->base = $this->base;
$controller->here = $this->base.'/'.$url;
$controller->action = $params['action'];
$controller->data = empty($params['data'])? null: $params['data'];
if ($missing_action)
{
$controller->missing_action = $params['action'];
$params['action'] = 'missing_action';
}
// initialize the controller
$controller->base = $this->base;
$controller->here = $this->base.'/'.$url;
$controller->params = $params;
$controller->action = $params['action'];
$controller->data = empty($params['data'])? null: $params['data'];
$controller->passed_args = empty($params['pass'])? null: $params['pass'];
// EXECUTE THE REQUESTED ACTION
call_user_func_array(array(&$controller, $params['action']), empty($params['pass'])? null: $params['pass']);
// EXECUTE THE REQUESTED ACTION
call_user_func_array(array(&$controller, $params['action']), empty($params['pass'])? null: $params['pass']);
$isFatal = isset($controller->isFatal) ? $controller->isFatal : false;
if ($isFatal)
{
switch($params['action'])
{
case 'missing_controller':
$this->errorUnknownController($url, $ctrl_name);
break;
case 'missing_action':
$this->errorUnknownAction($url, $ctrl_class, $controller->missing_action);
break;
}
}
if ($controller->autoRender)
{
$controller->render();
}
return $params;
return true;
}
/**
@ -166,7 +224,8 @@ class Dispatcher extends Object {
*
* @return string
*/
function baseUrl () {
function baseUrl ()
{
global $_SERVER;
//non mod_rewrite use:
@ -191,7 +250,8 @@ class Dispatcher extends Object {
* @param string $name Name of the error message (e.g. Not found)
* @param string $message
*/
function error ($code, $name, $message) {
function error ($code, $name, $message)
{
$controller = new Controller ($this);
$controller->base = $this->base;
$controller->error($code, $name, $message);
@ -203,7 +263,8 @@ class Dispatcher extends Object {
* @param unknown_type $url
* @param unknown_type $message
*/
function error404 ($url, $message) {
function error404 ($url, $message)
{
$this->error('404', 'Not found', sprintf(ERROR_404, $url, $message));
}
@ -212,7 +273,8 @@ class Dispatcher extends Object {
*
* @param string $url
*/
function errorNoController ($url) {
function errorNoController ($url)
{
DEBUG?
trigger_error (ERROR_NO_CONTROLLER_SET, E_USER_ERROR):
$this->error404($url, "no controller set");
@ -225,7 +287,8 @@ class Dispatcher extends Object {
* @param string $url
* @param string $controller_class
*/
function errorUnknownController ($url, $controller_class) {
function errorUnknownController ($url, $controller_class)
{
DEBUG?
trigger_error (sprintf(ERROR_UNKNOWN_CONTROLLER, $controller_class), E_USER_ERROR):
$this->error404($url, "missing controller \"{$controller_class}\"");
@ -237,7 +300,8 @@ class Dispatcher extends Object {
*
* @param string $url
*/
function errorNoAction ($url) {
function errorNoAction ($url)
{
DEBUG?
trigger_error (ERROR_NO_ACTION_SET, E_USER_ERROR):
$this->error404(sprintf(ERROR_404, $url, "no action set"));
@ -251,7 +315,8 @@ class Dispatcher extends Object {
* @param string $controller_class
* @param string $action
*/
function errorUnknownAction ($url,$controller_class, $action) {
function errorUnknownAction ($url,$controller_class, $action)
{
DEBUG?
trigger_error (sprintf(ERROR_NO_ACTION, $action, $controller_class), E_USER_ERROR):
$this->error404($url, "missing controller \"{$controller_class}\"");

View file

@ -163,30 +163,108 @@ class Template extends Object
* @param string $layout
* @param string $file Custom filename for view
*/
function render ($action=null, $layout=null, $file=null) {
function render ($action=null, $layout=null, $file=null)
{
if (isset($this->hasRendered) && $this->hasRendered)
{
return true;
}
else
{
$this->hasRendered = false;
}
$this->autoRender = false;
if (!$action) $action = $this->action;
if ($layout) $this->setLayout($layout);
//$isFatal = isset($this->isFatal) ? $this->isFatal : false;
$view_fn = $file? $file: $this->_getViewFn($action);
if (!is_file($view_fn)) {
DEBUG? trigger_error (sprintf(ERROR_NO_VIEW, $action, $view_fn), E_USER_ERROR)
: $this->error('404', 'Not found', sprintf(ERROR_404, '', "missing view \"{$action}\""));
die();
if (!is_file($view_fn))
{
if (strtolower(get_class($this)) == 'template')
{
return array('action' => $action, 'layout' => $layout, 'view_fn' => $view_fn);
}
// check to see if the missing view is due to a custom missing_action
if (strpos($action, 'missing_action') !== false)
{
$error_action = 'missing_action';
}
else
{
$error_action = 'missing_view';
}
// check for controller-level view handler
foreach(array($this->name, 'errors') as $view_dir)
{
$missing_view_fn = VIEWS.$view_dir.DS.$error_action.'.thtml';
$missing_view_exists = is_file($missing_view_fn);
if ($missing_view_exists)
{
break;
}
}
if (strpos($action, 'missing_view') === false)
{
$controller =& $this;
$controller->missing_view = $view_fn;
$controller->action = $action;
call_user_func_array(array(&$controller, 'missing_view'), empty($params['pass'])? null: $params['pass']);
$isFatal = isset($this->isFatal) ? $this->isFatal : false;
if (!$isFatal)
{
$view_fn = $missing_view_fn;
}
}
else
{
$missing_view_exists = false;
}
if (!$missing_view_exists || $isFatal)
{
// app/errors/missing_view.thtml view is missing!
if (DEBUG)
{
trigger_error (sprintf(ERROR_NO_VIEW, $action, $view_fn), E_USER_ERROR);
}
else
{
$this->error('404', 'Not found', sprintf(ERROR_404, '', "missing view \"{$action}\""));
}
die();
}
}
$out = $this->_render($view_fn, $this->_view_vars, 0);
if ($view_fn && !$this->hasRendered)
{
$out = $this->_render($view_fn, $this->_view_vars, 0);
if ($out !== false)
{
if ($this->layout && $this->autoLayout)
{
$out = $this->renderLayout($out);
}
if ($out !== false) {
if ($this->layout && $this->autoLayout)
$out = $this->renderLayout($out);
print $out;
}
else {
$out = $this->_render($view_fn, $this->_view_vars, false);
trigger_error (sprintf(ERROR_IN_VIEW, $view_fn, $out), E_USER_ERROR);
print $out;
$this->hasRendered = true;
}
else
{
$out = $this->_render($view_fn, $this->_view_vars, false);
trigger_error (sprintf(ERROR_IN_VIEW, $view_fn, $out), E_USER_ERROR);
}
return true;
}
}

View file

@ -1,5 +1,5 @@
BODY {
font-size:.9em;
font-size: 76%;
}
BODY, INPUT, TEXTAREA {
@ -8,15 +8,28 @@ font-family:sans-serif;
H1 {
font-size:2.1em;
text-align:center;
color: #fff;
text-align: left;
position: absolute;
padding-left: 15%;
top: 0.35em;
left: 0;
height: 4em;
}
H2 {
font-size:1.7em;
color: #383;
}
H3 {
font-size:1.4em;
color: #553;
}
H4 {
font-size:1.15em;
color: #338;
}
P {
@ -29,6 +42,10 @@ white-space:nowrap;
text-decoration:underline;
}
A:HOVER {
background-color:#EEE;
}
CODE, PRE {
font-family:monospace;
font-size:1.1em !important;
@ -55,3 +72,39 @@ HR {
height:0;
border-top:1px solid #AAA;
}
#container {
margin: 0 auto;
border-top: solid 5em #69c;
color: #333;
font: normal 1.1em Verdana;
padding: 0 15%;
}
#content {
padding-top: 1em;
width: 100%;
text-align: left;
line-height: 170%;
}
.notice {
padding: 1em;
background: #ffd;
border: solid 2px #eeb;
display: block;
font-family: Verdana;
}
.tip {
background: #efe;
padding: 1em;
border: solid 2px #cdc;
}
.error {
background: #fee;
padding: 1em;
border: solid 2px #dcc;
}