Add a 404 event to CWebApplication runController

Hi Guys,

I’ve been working on integrating Yii with some other systems (an old legacy system, WordPress, Drupal) where I’d like to have a controller action called before a 404 Exception is thrown. It would kind of be a catchAll controller action, but would be called after all other routes were tried.

I’ve looked through CWebApplication fairly thoroughly (catchAllRequest doesn’t work, as it’s used to redirect all pages to a temporary site down page or equivalent) and I think what I’m after is a new event added to CWebApplication’s runController:





	public function runController($route)

	{

		if(($ca=$this->createController($route))!==null)

		{

			list($controller,$actionID)=$ca;

			$oldController=$this->_controller;

			$this->_controller=$controller;

			$controller->init();

			$controller->run($actionID);

			$this->_controller=$oldController;

		}

		else

+		{

+			$event=new CEvent($this,$route);

+			$this->on404Error($event);

+			if(!$event->handled)				

				throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',

					array('{route}'=>$route===''?$this->defaultController:$route)));

+		}

	}


+	public function on404Error($event)

+	{

+		$this->raiseEvent('on404Error', $event);

+	}



This would allow a preloaded CApplicationComponent to catch 404 errors, try to run them through it’s own routing schema and then either handle the event or let Yii handle it. Unlike using the errorComponent, this could catch the CHttpException before it’s logged and handle it differently. Does that seem reasonable?

I know I can override CWebApplication for my app, but I wonder if this might be useful to have as part of the overall framework for other people to hook as well. Thoughts? Is there a better way to do this? I’m happy to submit a pull request on Github if you think it’s worth it.

Look here (see attached pdf) or here (full article in russian).

I think configuring error handler component and setting error action to your controller action should work in your situation.

I currently do set the error action, which does allow me to route to my default action. But my problem is that I’m getting errors in my logs (and via email) when in fact there are no errors. Thinking through it further (and thanks for the PDF, it was quite helpful), it looks like my problem is the placement of the Yii::log line in CApplication.php

A fix like this would patch it so I could catch 404 errors, try re-routing them through my own router and then not handle them if I couldn’t resolve things properly:





	public function handleException($exception)

	{

		// disable error capturing to avoid recursive errors

		restore_error_handler();

		restore_exception_handler();


-		$category='exception.'.get_class($exception);

-		if($exception instanceof CHttpException)

-			$category.='.'.$exception->statusCode;

-		// php <5.2 doesn't support string conversion auto-magically

-		$message=$exception->__toString();

-		if(isset($_SERVER['REQUEST_URI']))

-			$message.="\nREQUEST_URI=".$_SERVER['REQUEST_URI'];

-		if(isset($_SERVER['HTTP_REFERER']))

-			$message.="\nHTTP_REFERER=".$_SERVER['HTTP_REFERER'];

-		$message.="\n---";

-		Yii::log($message,CLogger::LEVEL_ERROR,$category);


		try

		{

			$event=new CExceptionEvent($this,$exception);

			$this->onException($event);

			if(!$event->handled)

			{

+				$category='exception.'.get_class($exception);

+				if($exception instanceof CHttpException)

+					$category.='.'.$exception->statusCode;

+				// php <5.2 doesn't support string conversion auto-magically

+				$message=$exception->__toString();

+				if(isset($_SERVER['REQUEST_URI']))

+					$message.="\nREQUEST_URI=".$_SERVER['REQUEST_URI'];

+				if(isset($_SERVER['HTTP_REFERER']))

+					$message.="\nHTTP_REFERER=".$_SERVER['HTTP_REFERER'];

+				$message.="\n---";

+				Yii::log($message,CLogger::LEVEL_ERROR,$category);

				// try an error handler

				if(($handler=$this->getErrorHandler())!==null)

					$handler->handle($event);

				else

					$this->displayException($exception);

			}

		}

		catch(Exception $e)

		{

			$this->displayException($e);

		}



This would then allow the exception handler to disable the exception (as it had been successfully caught and dealt with) or let it fall through. Does that seem like a reasonble solution?

Link to github issue for this - https://github.com/yiisoft/yii/issues/669

I also was in need for a custom 404 handling (without triggering exceptions) for redirection purposes and have overridden CWebApplication for that purpose.

So I second the implementation of some hook function like suggested here by the original poster.

Hi all,

I have the same problem than the original post

I want to handle the 404 error before it throw the exception, made some treatment to redirect the user to other link or suggest some pages. That’s why I need to override the runController function or handleException function. I am new with the behaviors and components.

Can you explain this for me?

Thanks a lot in advance.

@Samir, take a look at this Github repository. I have the basic items roughed in in that repository:

Also the Yii wiki article I link to from that repo has more details.

Thanks a lot acorncom,

I’ll check this and get back to you ASAP.

Thanks again.

this is good for customer and for SEO

some error can use too

Dear acorncom,

You really helped me, I used your handleException function.

Thank you so much my friend.

Samir IZZA