image Rio das Ostras, Brazil, 06/2010

Environments In Laravel 4

<p>Developers are defining their Laravel environments in many different ways, some of those are pretty smart and secure, but none, until now, have fit all my needs. So I would like to introduce something I was thinking about, it's an approach based in PHP only and, as soon as you set it up, you won't have to deal with environmental configuration in your application again, unless you need to alter values, of course.</p> <h3>What I've Been Seeing Around</h3> <p>There are many ways to configure an environment for your Laravel application. Laravel comes with this one:</p> <pre class="prettyprint"><code ><?php $env = $app->detectEnvironment(array( 'development' => array('dev.local'), 'production' => array('servername'), )); </code></pre> <p>But some people said this wasn't secure and it also had some problems, like environment collision between developers and forced you to edit it that every time you changed your site URL. So they found better way, which is now to store environment specific data in a <code class="spancode">.htaccess</code> file, or some configuration file related to the web server:</p> <pre class="prettyprint"><code > <IfModule mod_rewrite.c> #Options -MultiViews ... RewriteRule ^(.*)$ /index.php?/$1 [L] SetEnv LARAVEL_ENV "production" </IfModule> </code></pre> <p>And read it in your application:</p> <pre class="prettyprint"><code >$env = $app->detectEnvironment(function() { return getenv('LARAVEL_ENV') ?: 'local'; }); </code></pre> <p>But there are problems with this approach too: you must have access to enable mod_env in apache2 on your hosting provider; you must know how to to provide access to it in PHP; if you ever move to another provider or someone in your company decided to change from Apache to Nginx, you'll have to figure out how to configure the server, again, and, maybe, deal with <code class="spancode">fastcgi_param</code> instead of <code class="spancode">SetEnv</code>. In my opinion, you'll have trouble with it many times.</p> <p>Why can't this be simpler? Well... it can.</p> <h3>My Way</h3> <p>This is what I think you could do to avoid all this:</p> <p>Create a <code class="spancode">.environment</code> file in the root of your application and define your environment:</p> <pre class="prettyprint"><code > <?php return array( 'LARAVEL_ENV' => 'development', 'POSTGRESQL.HOST' => 'localhost', 'POSTGRESQL.DATABASE_NAME' => 'laraveldatabase', 'POSTGRESQL.DATABASE_USER' => 'laraveluser', 'POSTGRESQL.DATABASE_PASSWORD' => '!Bassw0rT', ); </code></pre> <p>Yeah, this is a PHP script file, but I prefer to not use the .php extension in this case, this is just a very important <a href="http://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory">dotfile</a> to me.</p> <p>Add it to your <code class="spancode">.gitignore</code> application file, so you don't risk having your passwords sent to Github, ever:</p> <pre class="prettyprint"><code > /bootstrap/compiled.php /vendor composer.phar composer.lock .DS_Store Thumbs.db .environment </code></pre> <p>Push your variables to the PHP environment (<code class="spancode">$_ENV</code>) by adding to your <code class="spancode">bootstrap/start.php</code> this loader code, before the <code class="spancode">$app->detectEnvironment()</code> call:</p> <pre class="prettyprint"><code >foreach(require __DIR__.'/../.environment' as $key => $value) { putenv(sprintf('%s=%s', $key, $value)); } </code></pre> <p>And you're done. Everything else you have set will work, as long as the keys are the same:</p> <pre class="prettyprint"><code >$env = $app->detectEnvironment(function() { return getenv('LARAVEL_ENV'); }); </code></pre> <p>You don't need a fallback to <strong>development</strong> here because if your <code class="spancode">.environment</code> file is not in place Laravel will raise a visible</p> <pre class="prettyprint"><code >Failed opening required '/var/www/application/../.environment' </code></pre> <p>And you won't ever forget to configure your environment, that's a bonus, believe me.</p> <p>Your database connections don't need to be separated in <strong>development</strong> and <strong>production</strong> anymore, because it can just make use of your new environmenf file:</p> <pre class="prettyprint"><code > <?php return array( 'connections' => array( 'postgresql' => array( 'driver' => 'pgsql', 'host' => getenv('POSTGRESQL.HOST'), 'database' => getenv('POSTGRESQL.DATABASE_NAME'), 'username' => getenv('POSTGRESQL.DATABASE_USER'), 'password' => getenv('POSTGRESQL.DATABASE_PASSWORD'), 'charset' => 'utf8', 'prefix' => '', 'schema' => 'public', ), ), ); </code></pre> <h3>Conclusion</h3> <p>This method might not be perfect, but here are some advantages of it:</p> <ul> <li>Once you set it up, you won't have to think about it again if your hosting, server, VM or domain changes.</li> <li>You won't have environment collisions between developers.</li> <li>You won't risk connecting on your production server while testing your app locally or in a staging server running under the same TLD.</li> <li>You won't ever have to deal with server configuration files again.</li> <li>You don't risk storing sensitive data on Github, Bitbucket or any other VCS.</li> <li>It's secure enough (if you think it's not, please tell me how is this less secure than a .htaccess file).</li> <li>It's simple, so simple that Laravel developers could just do that in the core. :)</li> </ul> <h3>Have fun!</h3>




comments powered by Disqus