ZF2 maintenance page - php

I try to set up a maintenance page with ZF2 but it's not working. I put a maintenance.html page in public folder (www) and in my onbootstrap function I've got the following code :
$config = $e->getApplication()->getServiceManager()->get('Appli\Config');
if($config['maintenance']) {
$response = $e->getResponse();
$response->getHeaders()->addHeaderLine('Location', '/maintenance.html');
$response->setStatusCode(503);
return $response;
}
I enter the if cause $config['maintenance'] is true but it's not displaying my maintenance.html page as expected. Instead it displays the page asked.
Is there something wrong about my redirection ?

You appear to be attempting to short-circuit the request directly from your onBootstrap method. That won't work, at that point the route hasn't been resolved and the controller hasn't been dispatched. Essentially, all you're doing is pre-populating the response, only for it to be over-written once the request is routed and dispatched.
If you want to affect the response, you'll need to listen to one of the other MvcEvents. It seems you want to do this before a controller is dispatched, so the place to do it would be in the EVENT_ROUTE, ideally with a high priority so it happens before the route is resolved by the router (saves wasted processing resolving a route that will never be dispatched).
public function onBootstrap(MvcEvent $e)
{
$events = $e->getApplication()->getEventManager();
$events->attach(MvcEvent::EVENT_ROUTE, function (MvcEvent $r) {
$config = $r->getApplication()->getServiceManager()->get('Appli\Config');
if ($config['maintenance']) {
$response = $r->getResponse();
// set content & status
$response->setStatusCode(503);
$response->setContent('<h1>Service Unavailable</h1>');
// short-circuit request...
return $response;
}
}, 1000);
}

You can't set a 503 status code and redirect - the two are mutually exclusive, as redirects use a 3xx status code.
You probably want something more like:
$response->setContent(file_get_contents('/path/to/maintenance.html'));
$response->setStatusCode(503);

Related

ZF2 routing and redirect function in controller

The following problem I am facing, error look likes this;
An error occurred
An error occurred during execution; please try again later.
Additional information:
Zend\Mvc\Exception\DomainException
File: .../vendor/zendframework/zendframework/library/Zend/Mvc/Controller/Plugin/Url.php:63
Message:
Url plugin requires that controller event compose a router; none found
I never faced this problem while I am trying to redirect from my controller. Lets say I implement the following function for redirection in my controller, which produces the error above;
public function __construct()
{
# Get user identity
$auth = new AuthenticationService();
if ($auth->hasIdentity()) {
$this->identity = $auth->getIdentity();
} else {
$this->redirect()->toRoute('admin/login');
}
}
The routing does exist as I can reach site.com/admin/login/ .. login is a child of admin so notation must be good. I am wondering what's going wrong and how to fix this issue or even, where to look for it would be a great starting point as well.
Thanks!
If you look at the error looks like you cant use the redirect plugin during the constructions of controller.
Url plugin requires that controller event compose a router; none found
might better to put that code in the onDispatch function like this.
public function onDispatch(MvcEvent $e)
{
# Get user identity
$auth = new AuthenticationService();
if ($auth->hasIdentity()) {
$this->identity = $auth->getIdentity();
} else {
return $this->redirect()->toRoute('admin/login');
}
return parent::onDispatch($e);
}
remember to return the redirect as otherwise the action will still be executed.

Laravel custom ModelNotFoundException handling per route group

I have two route groups in my Laravel application, one for an API and one for the website itself. I've added the following code in global.php for error handling of my API.
App::error(function(ModelNotFoundException $e)
{
return Response::json('', 404);
});
But not this obviously also has effect on my normal website, where I want to return a normal view when the ModelNotFoundException occurs. Like so:
App::error(function(ModelNotFoundException $e)
{
return Response::view('404.html', [], 404);
});
How can I setup different error handlers for different route groups?
I think you shouldn't care what part of the site that threw the error, but instead respond with whatever format the client requested. In general this is set in the Accepts header of the request and can be accessed in Laravel by:
if (Request::format() == 'json')
{
//
}
(above is taken from the documentation)
In your case, this would turn your error handling function to this:
App::error(function(ModelNotFoundException $e)
{
if (Request::format() == 'json') {
return Response::json('', 404);
} else {
return Response::view('404.html', [], 404);
}
});
This automatically covers you if, for instance, you add an non-API AJAX request to the main portion of your website (for whatever reason) that could potentially trigger a ModelNotFoundException. As long as your client sends the appropriate request headers, you're good.
You could try with changing environment. For selected group you could change environment using:
$app->setEnvironment('enviromentname');
and create new environmentname.php file in start directory
But I don't know if it will work.
You could also create a session variable and in App:error add code depending on this session variable:
$value = Session::get('type');
if ($value == 'onetype') {
// something
}
else {
// something else
}

Command pattern in php applications: how to handle controller actions?

I think this is more of a general question (so not php restricted) with regards to ddd and the command pattern.
Let's say I execute a CreatePostCommand from within the create action of my controller, the command will be handled and eventually executed successfully. What's the appropriate way to notify the controller which response to return in case the command did fail or succeed? Given the command handler will fire a domain specific event, I could hook up the controller to the event, but that seems a quite awkward, also not appropriate for every situation (e.g. a post could be created somewhere else and the controller really doesn't know about this :) ).
public function createAction($title, $content)
{
$this->commandBus->execute(new CreatePostCommand($title, $content);
$this->render('…'); // what if the command execution failed?
}
Any thoughts on this?
I think if you are really trying to follow the DDD command pattern then you need to treat the command bus as a fire and forget asynchronous process that may take a long time to complete.
Consider immediately redirecting to a command verifier controller. It's up to the command verifier to actively check the status of the command and see if it worked.
In most cases, the command will have finished successfully and your verifier can then redirect once again to continue normal flow.
If the command fails then the verifier puts up an appropriate error message.
If the command is in progress then you can entire a redirect loop while informing the user that the command is in progress.
Something like:
// Execute the command
$command = new CreatePostCommand($title, $content);
$this->commandBus->execute($command);
return redirect '/command-verifier/' . $command->getId();
// The verification action
public function verifyCommandAction($commandId)
$commandStatus = $this->commandBus->getStatus($commandId);
if ($commandStatus == SUCCESS) redirect to all is well;
if ($commandStatus == FAILED) then oops;
if ($commandStatus == IN_PROGRESS) then maybe pause a bit and redirect again while keeping the user informed.
Clearly there is quite a bit of hand waving going on but I think this is the most general approach especially with php where every request starts from ground zero.
The way I'm currently doing it is as follows (excuse long post).
public function createAction($title, $content) {
try {
$post = $this->commandBus->execute(new CreatePostCommand($title, $content);
}
catch (Exception $e) {
return $this->render('some error template file', $e);
}
return $this->render('successful creation template file', $post);
}
This way, you're creating a post and if everything goes as planned, return the $post object and send that to your view. On the other hand, when an exception is thrown during execution, you catch that error and send it to a view.
My preferred way is to have the controller call a method on a service that manages that behaviour, and have the controller injected as a listener that manages the responses, ie:
public function createAction($title, $content) {
$service = new CreateActionService($title, $content);
return $service->create($this);
}
public function onError(Exception $e) {
return $this->render('some error template file', $e);
}
public function onSuccess($post) {
return $this->render('success', $post);
}
Then in your service...
public function create($listener)
{
try {
$this->commandBus->execute(new CreatePostCommand($title, $content);
}
catch (Exception $e) {
return $this->listener->onError($e);
}
return $this->listener->onSuccess($post);
}
This way your service is managing the various results that the command handler may return, and your controller is left simply to manage the responses that you may wish returned to your presentation layer.

return from onBootStrap() in zend

I am calling one function from onBootStrap() to authorize user, in that function I am using header information to verify the user.
If this is not correct, I want to stop execution here(onBootStrap()) without even calling the actual API and return some response to the user .
User should get some response because then only user can know what's the problem.
How I can return response from there?
Simply said, onBootstrap is not sufficient for this. Usually, you have two stages in your application. The first is bootstrapping, the second is running. During run you can authorize users and return responses, during bootstrap this is not possible.
The reason is simple, you might have another module overriding it's behaviour. If you stop bootstrapping after your module, you can stop the execution of these modules. It's better to move the logic to run. This run stage is defined with various listeners, of which the first is route. There isn't much going on after bootstrap and before route, so in terms of performance it's neglectable.
A code example:
use Zend\Mvc\MvcEvent;
use Zend\Json\Json;
class Module
{
public function onBootstrap($e)
{
$app = $e->getApplication();
$em = $app->getEventManager();
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($app) {
// your auth logic here
if (!$auth) {
$response = $e->getResponse();
$response->setStatusCode(403);
$response->setContent(Json::encode(array(
'error' => 12345,
'message' => 'You are not authorized for this request',
));
return $response;
}
}, PHP_INT_MAX);
}
}
The listener is attached at an very early stage (PHP_INT_MAX) so the check happens as first in the complete route stage. You can also choose for quite a high number (like, 1000) so you can hook in this event before user authorization.

Preventing error pages caching when using Zend_Cache_Backend_Static

We're currently running an app that caches pages to static html files using Zend_Cache_Backend_Static. This works really well, except that our cache is getting filled with hundreds of empty files and folders when incorrect urls are requested. Is there any way to prevent a page being cached if an Exception is being thrown? I was surprised to discover that this wasn't standard behaviour.
I've done a little digging and the ZF code that actually deals with saving out the static html pages is as follows in Zend_Cache_Frontend_Capture:
public function _flush($data) {
$id = array_pop($this->_idStack);
if ($id === null) {
Zend_Cache::throwException('use of _flush() without a start()');
}
if ($this->_extension) {
$this->save(serialize(array($data, $this->_extension)), $id, $this->_tags);
} else {
$this->save($data, $id, $this->_tags);
}
return $data;
}
This function is the output_callback for ob_start. I've tried getting hold of the response object to test for status but it doesn't seem to work inside _flush.
$response = Zend_Controller_Front::getInstance()->getResponse();
if($response->getStatus() == '200') {
// do the save as normal
}
else {
// do nothing
return false;
}
My only other thought was to test the length of $data, only caching if strlen($data) > 0 seems to work but it doesn't feel robust enough.
Update:
Unfortunately by the time we hit the ErrorController the static page has already been written to the cache, so disabling the cache at that point won't work. However it is possible to remove the page based on $_SERVER['REQUEST_URI'], which is what is used as an id when the page is first written. This line can be added to the start of errorAction in the ErrorController:
$this->_helper->cache->removePage($_SERVER['REQUEST_URI'], true);
It works nicely, but I'd prefer not to write the page in the first place!
From further experimentation the problem is not down to standard Zend Framework exceptions that cause 404s (ie. Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE, Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER, Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION) but to my custom exceptions. This is now really obvious now that I think about it, as Zend_Cache_Backend_Static needs to be initialised in the init method of an action controller. Any situation where there is no route, controller or action it won't ever be initialised anyway.
I'm throwing exceptions in existing actions where a user may be querying for a non-existent article. Therefore caching has been enabled in init and the page has been written by the time we hit postDispatch in a Front Controller Plugin (still not sure why this is the case it just is) so I can't cancel at that point. One solution then is to cancel the cache at the point of throwing the exception. The standard method of managing static page caching is using the Zend_Controller_Action_Helper_Cache action helper. I've extended this to add a cancel method like so:
<?php
class Zend_Controller_Action_Helper_PageCache extends Zend_Controller_Action_Helper_Cache {
public function cancel() {
$cache = $this->getCache(Zend_Cache_Manager::PAGECACHE);
$cache->setOption('caching', false);
$cache->getBackend('disable_caching', true);
}
}
My action controller now looks like this:
<?php
class IndexController extends Zend_Controller_Action {
private $_model;
public function init() {
$this->_model = new Model();
// using extended pageCache rather than $this->_helper->cache:
$this->_helper->pageCache(array('index'), array('indexaction'));
}
public function indexAction() {
$alias = $this->_request->getParam('article');
$article = $this->_model->getArticleByAlias($alias);
if(!$article) {
// new cancel method will disable caching
$this->_helper->pageCache->cancel();
throw new Zend_Controller_Action_Exception('Invalid article alias', 404);
}
$this->view->article = $article;
}
}
You should alter your .htaccess file RewriteRules to check for filesizes with option -s
This way if an error should occur when a page is being cached (thus producing a 0 byte file) it won't permanently be stored in the cache.
If you are using the standard ErrorController to handle 404, 500, and unhandled exceptions, and you can get a reference to your cache object from there, you could disable caching from the error handler.
In your error controller (or wherever you would like to cancel caching from), try:
$cache->setOption('caching', false);
When the save() metod of Zend_Cache_Core is called by Zend_Cache_Frontend_Capture::_flush(), it will see the caching option is set to false and it will not actually save the data to the cache and return true.

Categories