├── .gitignore ├── .travis.yml ├── phpunit.xml.dist ├── composer.json ├── LICENSE ├── src └── Robots.php ├── README.md └── tests └── League └── StackRobots └── RobotsTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | /composer.phar 4 | /composer.lock 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | 9 | before_script: composer install --prefer-source 10 | 11 | script: phpunit 12 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests/League/StackRobots 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | src/ 14 | 15 | 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "league/stack-robots", 3 | "description": "StackRobots Middleware for StackPHP", 4 | "license": "MIT", 5 | "require": { 6 | "php": ">=5.3.10", 7 | "symfony/http-foundation": "~2.1", 8 | "symfony/http-kernel": "~2.1" 9 | }, 10 | "require-dev" : { 11 | "stack/builder" : "1.0.*@dev", 12 | "stack/callable-http-kernel": "~1.0@dev", 13 | "stack/run": "~1.0@dev" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "League\\StackRobots\\": "src/" 18 | } 19 | }, 20 | "extra": { 21 | "branch-alias": { 22 | "dev-master": "1.0-dev" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2013 Don Gilbert (@dilbert4life) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/Robots.php: -------------------------------------------------------------------------------- 1 | app = $app; 36 | $this->env = $env; 37 | $this->envVar = $envVar; 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) 44 | { 45 | if (getenv($this->envVar) !== $this->env) { 46 | if ($request->getPathInfo() === '/robots.txt') { 47 | return new Response("User-Agent: *\nDisallow: /", 200, array('Content-Type' => 'text/plain')); 48 | } 49 | 50 | $response = $this->app->handle($request, $type, $catch); 51 | 52 | $response->headers->set('X-Robots-Tag', 'noindex, nofollow, noarchive'); 53 | 54 | return $response; 55 | } 56 | 57 | return $this->app->handle($request, $type, $catch); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # League\StackRobots 2 | 3 | [![Build Status](https://travis-ci.org/php-loep/StackRobots.png?branch=master)](https://travis-ci.org/php-loep/StackRobots) 4 | [![Total Downloads](https://poser.pugx.org/league/stack-robots/downloads.png)](https://packagist.org/packages/league/stack-robots) 5 | [![Latest Stable Version](https://poser.pugx.org/league/stack-robots/v/stable.png)](https://packagist.org/packages/league/stack-robots) 6 | 7 | StackRobots is a middleware for [StackPHP](http://stackphp.com). It provides a default robots.txt for 8 | non-production environments. 9 | 10 | ## Install Via Composer 11 | 12 | ```json 13 | { 14 | "require": { 15 | "league/stack-robots": "~1.0" 16 | } 17 | } 18 | ``` 19 | 20 | ## Usage 21 | 22 | StackRobots is a very simple middleware. By default it looks at the `SERVER_ENV` environment variable, 23 | and if the `SERVER_ENV` does not equal production, it captures the response and sets an `X-Robots-Tag` 24 | header with a value of `noindex, nofollow, noarchive`. 25 | 26 | When you push the middleware on to the stack, you can pass 2 additional parameters, `$env` and `$envVar`. 27 | The `$env` parameter is the environment in which you want this middleware to not do anything, typically 28 | `production`. The `$envVar` parameter is the environment variable that holds the environment of the 29 | current server; it defaults to `SERVER_ENV`. 30 | 31 | If the value of `SERVER_ENV` matches the value that is passed, this middleware will just pass control on 32 | to the next middleware. However, if it does not match, then StackRobots will set the `X-Robots-Tag`. 33 | Additionally, if the incoming request is for your `/robots.txt` file, then StackRobots will stop the request 34 | and send the following response. 35 | 36 | ```php 37 | return new Response("User-Agent: *\nDisallow: /", 200, array('Content-Type' => 'text/plain')); 38 | ``` 39 | And this is what the browser receives. 40 | ```txt 41 | User-Agent: * 42 | Disallow: / 43 | ``` 44 | 45 | > More info on the `X-Robots-Tag` is available [here](https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag). 46 | 47 | ## Example 48 | 49 | ```php 50 | include_once '../vendor/autoload.php'; 51 | 52 | use Symfony\Component\HttpFoundation\Request; 53 | use Symfony\Component\HttpFoundation\Response; 54 | use League\StackRobots\Robots; 55 | 56 | $app = new Stack\CallableHttpKernel(function (Request $request) { 57 | return new Response('Hello World!'); 58 | }); 59 | 60 | putenv('SERVER_ENV=dev'); 61 | 62 | $app = (new Stack\Builder) 63 | ->push('League\\StackRobots\\Robots') 64 | ->resolve($app); 65 | 66 | Stack\run($app); 67 | ``` 68 | 69 | ## Authors 70 | 71 | - Don Gilbert [@dilbert4life](http://twitter.com/dilbert4life) 72 | - Inspired by [Cylon](https://github.com/dmathieu/cylon) for Ruby. -------------------------------------------------------------------------------- /tests/League/StackRobots/RobotsTest.php: -------------------------------------------------------------------------------- 1 | getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); 26 | 27 | $this->fixture = new Robots($mockApp); 28 | } 29 | 30 | /** 31 | * The app property should be an instanceof HttpKernelInterface 32 | * 33 | * @return void 34 | */ 35 | public function testConstructPassesApp() 36 | { 37 | $this->assertAttributeInstanceOf( 38 | 'Symfony\\Component\\HttpKernel\\HttpKernelInterface', 39 | 'app', 40 | $this->fixture, 41 | 'The app property should be an instanceof HttpKernelInterface' 42 | ); 43 | } 44 | 45 | /** 46 | * The env property should be set to the value set in the constructor. 47 | * 48 | * @return void 49 | */ 50 | public function testConstructDefaultEnv() 51 | { 52 | $this->assertAttributeEquals( 53 | 'production', 54 | 'env', 55 | $this->fixture, 56 | 'The env property should be set to the value set in the constructor.' 57 | ); 58 | } 59 | 60 | /** 61 | * The envVar property should be set to the value set in the constructor. 62 | * 63 | * @return void 64 | */ 65 | public function testConstructDefaultEnvVar() 66 | { 67 | $this->assertAttributeEquals( 68 | 'SERVER_ENV', 69 | 'envVar', 70 | $this->fixture, 71 | 'The envVar property should be set to the value set in the constructor.' 72 | ); 73 | } 74 | 75 | /** 76 | * The env property should be set to the value passed to the constructor. 77 | * 78 | * @return void 79 | */ 80 | public function testConstructPassesEnv() 81 | { 82 | $mockApp = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); 83 | 84 | $this->fixture = new Robots($mockApp, 'testing'); 85 | 86 | $this->assertAttributeEquals( 87 | 'testing', 88 | 'env', 89 | $this->fixture, 90 | 'The env property should be set to the value passed to the constructor.' 91 | ); 92 | } 93 | 94 | /** 95 | * The envVar property should be set to the value passed to the constructor. 96 | * 97 | * @return void 98 | */ 99 | public function testConstructPassesEnvVar() 100 | { 101 | $mockApp = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); 102 | 103 | $this->fixture = new Robots($mockApp, 'testing', 'MY_ENVVAR'); 104 | 105 | $this->assertAttributeEquals( 106 | 'MY_ENVVAR', 107 | 'envVar', 108 | $this->fixture, 109 | 'The envVar property should be set to the value passed to the constructor.' 110 | ); 111 | } 112 | 113 | /** 114 | * Test a request for robots.txt sends the proper response. 115 | * 116 | * @return void 117 | */ 118 | public function testHandleSendsResponse() 119 | { 120 | $mockApp = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); 121 | $mockRequest = $this->getMock('Symfony\Component\HttpFoundation\Request', array('getPathInfo')); 122 | 123 | $mockRequest->expects($this->once()) 124 | ->method('getPathInfo') 125 | ->will($this->returnValue('/robots.txt')); 126 | 127 | $this->fixture = new Robots($mockApp, 'testing'); 128 | 129 | $response = $this->fixture->handle($mockRequest); 130 | 131 | $expectedResponse = new Response("User-Agent: *\nDisallow: /", 200, array('Content-Type' => 'text/plain')); 132 | 133 | $this->assertEquals( 134 | $expectedResponse, 135 | $response, 136 | 'When requesting the robots.txt file, the proper response should be received.' 137 | ); 138 | } 139 | 140 | /** 141 | * Test a typical handling of a request. 142 | * 143 | * @return void 144 | */ 145 | public function testHandle() 146 | { 147 | $mockApp = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface', array('handle')); 148 | $mockRequest = $this->getMock('Symfony\Component\HttpFoundation\Request'); 149 | 150 | $mockApp->expects($this->once()) 151 | ->method('handle') 152 | ->will($this->returnValue('foo')); 153 | 154 | $this->fixture = new Robots($mockApp); 155 | 156 | $this->assertSame( 157 | 'foo', 158 | $this->fixture->handle($mockRequest), 159 | 'When in a production environment, the handler should be passed on to the next middleware.' 160 | ); 161 | } 162 | 163 | /** 164 | * Tests that a X-Robots-Tag is present in the response 165 | * 166 | * @return void 167 | */ 168 | public function testHandleSetsXRobotsTagHeader() 169 | { 170 | $mockApp = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface', array('handle')); 171 | $mockRequest = $this->getMock('Symfony\Component\HttpFoundation\Request'); 172 | $mockResponse = $this->getMock('Symfony\Component\HttpFoundation\Response'); 173 | 174 | $mockApp->expects($this->once()) 175 | ->method('handle') 176 | ->will($this->returnValue($mockResponse)); 177 | 178 | $this->fixture = new Robots($mockApp, 'testing'); 179 | 180 | $response = $this->fixture->handle($mockRequest); 181 | 182 | $this->assertAttributeContains( 183 | 'X-Robots-Tag', 184 | 'headerNames', 185 | $response->headers, 186 | 'When not in the production environment, a X-Robots-Tag header should be set.', 187 | true // ignores case 188 | ); 189 | } 190 | } 191 | --------------------------------------------------------------------------------