Skip to content

Commit

Permalink
Merge pull request #12 from zf-fr/add-events
Browse files Browse the repository at this point in the history
Add events
  • Loading branch information
bakura10 committed Nov 21, 2013
2 parents 3537ca4 + 92f9b48 commit 49de6a7
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 5 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

* [BC] Remove Google Bot, Yahoo and BingBot from the crawler's list, because those search engines
support _escaped_fragment_ and want to ensure people aren't penalized for cloaking
* The priority the listener is registered has been lowered from 10000 to 1000.
* Prerender listener now triggers two events: "prerender.pre" and "prerender.post". This allows you to
cache the response and return it without hitting the Prerender service
* The priority the listener is registered has been lowered from 10000 to 1000

# 1.1.2

Expand Down
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,62 @@ return array(
> Note: remember to specify URL here and not ZF2 route names. This occur because ZfrPrerender registers a listener
that happen very early in the MVC process, before the routing is actually done.

### Events

`ZfrPrerender\Mvc\PrerenderListener` triggers two events:

1. `ZfrPrerender\Mvc\PrerenderListener::EVENT_PRERENDER_PRE`: this event is triggered before actually making the
request to Prerender service. If you return a `Zend\Http\Response` object from the listener attached to this event,
it will immediately return this response, hence avoiding a new request to the Prerender service.
2. `ZfrPrerender\Mvc\PrerenderListener::EVENT_PRERENDER_POST`: this event is triggered once the response from the
Prerender service is made. This allows you to cache it (for instance in Memcached).

Listeners attached to those two events receive an instance of `ZfrPrerender\Mvc\PrerenderEvent`. Here is an example
that shows you how to register listeners using the shared event manager. In your `Module.php` class:

```php
public function onBootstrap(MvcEvent $event)
{
$eventManager = $event->getTarget()->getEventManager();
$sharedManager = $eventManager->getSharedManager();

$sharedManager->attach(
'ZfrPrerender\Mvc\PrerenderListener',
PrerenderListener::EVENT_PRERENDER_PRE,
array($this, 'prerenderPre')
);

$sharedManager->attach(
'ZfrPrerender\Mvc\PrerenderListener',
PrerenderListener::EVENT_PRERENDER_POST,
array($this, 'prerenderPost')
);
}

public function prerenderPre(PrerenderEvent $event)
{
$request = $event->getRequest();

// Check from your cache if you have already the content
// $content = ...

$response = new Response();
$response->setStatusCode(200);
$response->setContent($content);

return $response;
}

public function prerenderPost(PrerenderEvent $event)
{
// This is the response we get from the Prerender service
$response = $event->getResponse();

// You could get the body and put it in cache
// ...
}
```

### Testing

If you want to make sure your pages are rendering correctly:
Expand Down
78 changes: 78 additions & 0 deletions src/ZfrPrerender/Mvc/PrerenderEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfrPrerender\Mvc;

use Zend\EventManager\Event;
use Zend\Http\Request as HttpRequest;
use Zend\Http\Response as HttpResponse;

/**
* Event that is thrown before and after a Prerender.io request
*
* @author Michaël Gallego
* @licence MIT
*/
class PrerenderEvent extends Event
{
/**
* Event constants
*/
const EVENT_PRERENDER_PRE = 'prerender.pre';
const EVENT_PRERENDER_POST = 'prerender.post';

/**
* @var HttpRequest
*/
protected $request;

/**
* @var HttpResponse|null
*/
protected $response;

/**
* @param HttpRequest $request
* @param HttpResponse $response
*/
public function __construct(HttpRequest $request, HttpResponse $response = null)
{
$this->request = $request;
$this->response = $response;
}

/**
* Get the request
*
* @return HttpRequest
*/
public function getRequest()
{
return $this->request;
}

/**
* Get the response
*
* @return HttpResponse|null
*/
public function getResponse()
{
return $this->response;
}
}
50 changes: 47 additions & 3 deletions src/ZfrPrerender/Mvc/PrerenderListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
namespace ZfrPrerender\Mvc;

use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\Http\Client as HttpClient;
use Zend\Http\Request as HttpRequest;
use Zend\Http\Response as HttpResponse;
use Zend\Mvc\MvcEvent;
use Zend\Stdlib\RequestInterface;
use Zend\Stdlib\ResponseInterface;
Expand All @@ -35,7 +38,7 @@
* @author Michaël Gallego
* @licence MIT
*/
class PrerenderListener extends AbstractListenerAggregate
class PrerenderListener extends AbstractListenerAggregate implements EventManagerAwareInterface
{
/**
* @var ModuleOptions
Expand All @@ -47,6 +50,11 @@ class PrerenderListener extends AbstractListenerAggregate
*/
protected $httpClient;

/**
* @var EventManagerInterface
*/
protected $eventManager;

/**
* @param ModuleOptions $options
*/
Expand Down Expand Up @@ -89,7 +97,7 @@ public function getHttpClient()
}

/**
* Prerender the page
* Pre-render the page
*
* @param MvcEvent $event
* @return void|ResponseInterface
Expand All @@ -103,14 +111,29 @@ public function prerenderPage(MvcEvent $event)
}

$event->stopPropagation(true);
$eventManager = $this->getEventManager();

// Trigger a pre-event (for creating a response from cache, for instance)
$responses = $eventManager->trigger(PrerenderEvent::EVENT_PRERENDER_PRE, new PrerenderEvent($request));

if ($responses->last() instanceof HttpResponse) {
return $responses->last();
}

// Make the actual request to Prerender service
$client = $this->getHttpClient();
$uri = rtrim($this->moduleOptions->getPrerenderUrl(), '/') . '/' . $request->getUriString();

$client->setUri($uri)
->setMethod(HttpRequest::METHOD_GET);

return $client->send();
$response = $client->send();

// Trigger a post-event (for putting in cache the response, for instance)
$prerenderEvent = new PrerenderEvent($request, $response);
$eventManager->trigger(PrerenderEvent::EVENT_PRERENDER_POST, $prerenderEvent);

return $prerenderEvent->getResponse();
}

/**
Expand Down Expand Up @@ -226,4 +249,25 @@ protected function isBlacklisted($uri, $referer, array $blacklistUrls)

return false;
}

/**
* {@inheritDoc}
*/
public function setEventManager(EventManagerInterface $eventManager)
{
$eventManager->setIdentifiers(array(__CLASS__, get_class($this)));
$this->eventManager = $eventManager;
}

/**
* {@inheritDoc}
*/
public function getEventManager()
{
if (null === $this->eventManager) {
$this->setEventManager(new EventManager());
}

return $this->eventManager;
}
}
42 changes: 42 additions & 0 deletions tests/ZfrPrerenderTest/Mvc/PrerenderEventTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfrPrerenderTest\Mvc;

use PHPUnit_Framework_TestCase as TestCase;
use ZfrPrerender\Mvc\PrerenderEvent;

/**
* @author Michaël Gallego <[email protected]>
*
* @covers \ZfrPrerender\Mvc\PrerenderEvent
* @group Coverage
*/
class PrerenderEventTest extends TestCase
{
public function testGetters()
{
$request = $this->getMock('Zend\Http\Request');
$response = $this->getMock('Zend\Http\Response');

$event = new PrerenderEvent($request, $response);

$this->assertSame($request, $event->getRequest());
$this->assertSame($response, $event->getResponse());
}
}
42 changes: 41 additions & 1 deletion tests/ZfrPrerenderTest/Mvc/PrerenderListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

use PHPUnit_Framework_TestCase as TestCase;
use Zend\EventManager\EventManager;
use Zend\EventManager\ResponseCollection;
use Zend\Http\Request as HttpRequest;
use Zend\Mvc\MvcEvent;
use ZfrPrerender\Mvc\PrerenderEvent;
use ZfrPrerender\Mvc\PrerenderListener;
use ZfrPrerender\Options\ModuleOptions;
use ZfrPrerenderTest\Util\ServiceManagerFactory;
Expand Down Expand Up @@ -289,11 +291,49 @@ public function testCanPerformGetRequest()

$clientMock->expects($this->once())
->method('send')
->will($this->returnValue($this->getMock('Zend\Stdlib\ResponseInterface')));
->will($this->returnValue($this->getMock('Zend\Http\Response')));

$listener->setHttpClient($clientMock);

$response = $listener->prerenderPage($mvcEvent);
$this->assertInstanceOf('Zend\Stdlib\ResponseInterface', $response);
}

public function testSetCorrectIdentifiers()
{
$listener = new PrerenderListener(new ModuleOptions());
$listener->setEventManager(new EventManager());

$eventManager = $listener->getEventManager();

$this->assertEquals(array('ZfrPrerender\Mvc\PrerenderListener'), $eventManager->getIdentifiers());
}

public function testTriggerEventsAndStopIfResponseIsReturned()
{
$mvcEvent = new MvcEvent();
$request = new HttpRequest();

$request->setUri('http://www.example.com');
$request->getHeaders()->addHeaderLine('User-Agent', 'Baiduspider+(+http://www.baidu.com/search/spider.htm)');
$mvcEvent->setRequest($request);

$eventManager = $this->getMock('Zend\EventManager\EventManagerInterface');

$moduleOptions = ServiceManagerFactory::getServiceManager()->get('ZfrPrerender\Options\ModuleOptions');

$listener = new PrerenderListener($moduleOptions);
$listener->setEventManager($eventManager);

$response = $this->getMock('Zend\Http\Response');
$responseCollection = new ResponseCollection();
$responseCollection->push($response);

$eventManager->expects($this->once())
->method('trigger')
->with(PrerenderEvent::EVENT_PRERENDER_PRE)
->will($this->returnValue($responseCollection));

$this->assertSame($response, $listener->prerenderPage($mvcEvent));
}
}

0 comments on commit 49de6a7

Please sign in to comment.