Now that Google App Engine opens to the PHP world and that the 4th version of the Laravel framework is officialy launched, we wanted to test these two promising technologies together at Neoxia.

This blog post is the result of our tests whose goal was to have a standard Laravel 4 application on the Google App Engine PHP runtime up and running.
Given that an application without a database is not very useful, we also tested the Google Cloud SQL service which is the Google flavour of the MySQL database in the cloud.

This blog is not a tutorial nor for Google AppEngine, nor for Laravel 4, and we invite you to consult if needed their respective documentation.

Install App Engine for PHP and Laravel 4

To download and install App Engine and the Laravel 4 framework, please check the following documentation.

With these two components, you will be able to follow the different steps of this blog post and to run tests on your local environment.

First attempts

First, we will create a new Laravel 4 application.

composer create-project laravel/laravel

To allow an application to run on App Engine, we have to add an app.yaml file in the root directory of the application.

app.yaml

application: gae-laravel4-demo
version: 1
runtime: php
api_version: 1

handlers:

- url: /favicon\.ico
  static_files: public/favicon.ico
  upload: public/favicon\.ico

- url: /.*
  script: public/index.php

In our case, the application name is gae-laravel4-demo, but you need to change it by the one you have chosen for your application before uploading it.

After this first configuration step, we can launch the application by using the Web server included in the App Engine SDK. You can test the application at this url http://localhost:8080.
In our environment, the command to launch the application is given below. You need to adapt it to your own environement.

../google_appengine/dev_appserver.py --php_executable_path=/usr/local/opt/php54/bin/php-cgi .

At this stage, the application seems to be running perfectly !
Actually, we can see the Laravel 4 default page … take note that the famous “Hello world !” message has disappeared !

laravel4-hello-72dpi

When we got this first result, we have been quite impressed by the ease with which a framework such as Laravel could run on App Engine with little change.
Unfortunately, like the rest of this blog will show, this was just an optical illusion.

First upload to Google App Engine

If you don’t have done it before, we invite you to create an App Engine application (https://appengine.google.com/) and to subscribe to the App Engine for PHP beta program (https://gaeforphp.appspot.com/) in order to allow the upload of a PHP application to your App Engine instance. Indeed, the PHP runtime that has been announced at the last Google I/O conference is still in beta and Google authorizes progressively its roll out.

We then upload the application to the App Engine using the following command that you need to adapt to your environment.

../google_appengine/appcfg.py update -R --runtime=php ./

When we try to access the application through the gae-laravel4-demo.appspot.com url, an error 500 is returned.
An analysis of the App Engine logs gives us the cause of the error. The PHP gethostname() function is unknown.
This error is unexpected because the gethostname() function is available since PHP 5.3 and the PHP runtime of App Engine is based on version 5.4. In addition, we haven’t found anything in the App Engine for PHP documentation indicating that this function had been disabled.

Never mind, to work around this error, we just need to provide an implementation of the gethostname() function, which we did by editing bootstrap/start.php.

<?php

// Adaptation for the App Engine PHP runtime
// Add the gethostname function if it does not exist
if (!function_exists('gethostname')) {
    function gethostname() {
        return php_uname('n');
    }
}

The logs analysis gives us another information concerning the php_sapi_name() function which is not allowed by default.

In order to authorize this function, as well as other PHP functions that would be disabled by default in the App Engine PHP runtime, you need to add a php.ini file in the root directory of your application in which you list the functions to be activated. Take note that we also activate the php_uname() function which is used in our implementation of the gethostname() function.

php.ini

; enable function that are disabled by default in the App Engine PHP runtime
google_app_engine.enable_functions = "php_sapi_name, php_uname"

The mcrypt PHP extension

After these first changes, we have uploaded our application App Engine and have tried to access the application url.
This time, the error 500 has disappeared, but we got a web page with the following message :

“Laravel requires the Mcrypt PHP extension.”

In fact, the Laravel 4 encryption module is based on the mcrypt PHP extension which is not available in App Engine PHP runtime and when the Laravel 4 framework starts, it checks the presence of the mcrypt extension and dies if it is not available.

A solution is to replace the Illuminate\Encryption package which uses the mcrypt extension by the neoxia\laravel-openssl-encryption package, created by Neoxia, and which uses the openssl PHP extension which is available in the App Engine PHP runtime.

Here is the installation process of this package.

You need to add the neoxia\laravel-openssl-encryption package to the composer.json file located in the root directory of your application.

"require": {
    "laravel/framework": "4.0.*",
    "neoxia/laravel-openssl-encryption": "1.0.*"
},

Then, you need to execute again the composer update command.

You also need to update the app/config/app.php file as shown below.


'providers' => array(

    ...
    //'Illuminate\Encryption\EncryptionServiceProvider',
    'Neoxia\LaravelOpensslEncryption\LaravelOpensslEncryptionServiceProvider',
    ...

Finally, it is needed to remove the test which is performed by Laravel about the availability of the mcrypt extension.
Unfortunately, this test is performed at the heart of the framework startup process and it requires to slightly update the start.php file located in the vendor\laravel\framework\src\Illuminate\Foundation directory.


/*
|--------------------------------------------------------------------------
| Check Extensions
|--------------------------------------------------------------------------
|
| Laravel requires a few extensions to function. Here we will check the
| loaded extensions to make sure they are present. If not we'll just
| bail from here. Otherwise, Composer will crazily fall back code.
|
*/

if ( ! ( extension_loaded('mcrypt') or extension_loaded('openssl') ) )
{
    die('Laravel requires one of the Mcrypt or Openssl PHP extension.'.PHP_EOL);

    exit(1);
}

Note : during an update of the Laravel framework by using the composer update command, the modified start.php file will be reset to its default content. Thus, it will be necessary to update again the start.php file after any framework update and before any upload to App Engine.

After these last updates, we have uploaded the application to App Engine and have tried to access the application url.
The error relative to lack of the mcrypt extension has disappeared, but an exception has been thrown that indicates that it is forbidden to write on the App Engine file system.

The Laravel 4 logger

Laravel 4 uses the Monolog package to log the errors and the other user messages.
By default, the Monolog RotatingFileHandler handler is used to create daily log files on the file system. However, on App Engine, it is forbidden to write on the file system and we have to use the syslog() PHP function instead.

Here, Monolog is going to be of great help, because it includes the SyslogHandler handler that we will use by updating the app/start/global.php file as follows.

app/start/global.php


/*
|--------------------------------------------------------------------------
| Application Error Logger
|--------------------------------------------------------------------------
|
| Here we will configure the error logger setup for the application which
| is built on top of the wonderful Monolog library. By default we will
| build a rotating log file setup which creates a new file each day.
|
*/

// we comment the default file logger
//$logFile = 'log-'.php_sapi_name().'.txt';

//Log::useDailyFiles(storage_path().'/logs/'.$logFile);

/*
    Replace the default Log file handler by the syslog handler to be 
used in the google appengine environment
*/
$monolog = Log::getMonolog();
$monolog->pushHandler(new Monolog\Handler\SyslogHandler('intranet', 'user', Logger::DEBUG, false, LOG_PID));

Laravel 4 session and cache managers

By default, Laravel 4 records the values in session by writing them in files, which is forbidden by App Engine.
To fix this issue, you need to use the session manager based on Memcached included in the Laravel 4 framework.
To do this, just update the app/config/session.php file as follows.

app/config/session.php


/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "native", "cookie", "database", "apc",
|            "memcached", "redis", "array"
|
*/

'driver' => 'memcached',

Likewise, the default Laravel 4 cache manager uses the file system to record the values stored in the cache.
Again, it is needed to use the cache manager based on Memcached included in the Laravel 4 framework.
To do this, just update the app/config/cache.php file as follows.

app/config/cache.php


/*
|--------------------------------------------------------------------------
| Default Cache Driver
|--------------------------------------------------------------------------
|
| This option controls the default cache "driver" that will be used when
| using the Caching library. Of course, you may use other drivers any
| time you wish. This is the default when another is not specified.
|
| Supported: "file", "database", "apc", "memcached", "redis", "array"
|
*/

'driver' => 'memcached',

The bootstrap/compiled.php file

This file is generated by the Laravel 4 framework after its first installation and after any update. It collects within a single file the most used classes of the framework in order to load them as fast as possible.
Take note that this file remains an option and that the application can run without it.

After the updates we made to the Laravel framework configuration, we need to regenerate the bootstrap/compiled.php file by using the following command.

./artisan optimize

End of the first stage

At this stage, we just need to upload again the application to App Engine and to access the application url in order to finally get the expected message.

laravel4-hello-72dpi

And now, Google Cloud SQL !

We are now going to add to our application a connection to a Google Cloud SQL database.

Before this, you need to create a Google Cloud SQL instance from the Google APIs web console (https://code.google.com/apis/console) and associate your newly created instance to your App Engine application.
Note : the App Engine application and the Google Cloud SQL instance should belong to the same region (US or EU).

Then, you need to create your database by using the SQL command ‘CREATE DATABASE dbname;’ where dbname is the name of your database. To do this, you can use the Google Cloud SQL web console or let your application create the database programmatically.

Once the MySQL instance is created, associated to the application, and that your database has been created, you need to configure the Laravel 4 framework to access the database. To do this, you need to update the app/config/database.php file in the following manner, taking care to replace in the code below the values my-project-name:my-cloud-instance and dbname by the values corresponding to your newly created database.


...
'mysql' => array(
    'driver'    => 'mysql',
    'unix_socket' => '/cloudsql/my-project-name:my-cloudsql-instance',
    'host'      => '',
    'database'  => 'dbname',
    'username'  => 'root',
'password'  => '',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
),
...

In order to test our application, we have created a table with some data.

CREATE TABLE gaelaravel4demo.people (id INT, first_name VARCHAR(50), last_name VARCHAR(50));
INSERT gaelaravel4demo.people VALUES (1, ‘John’, ‘Doe’), (2, ‘Paul’, ‘Smith’);

We have also added the Person Model in the app/models directory.

app/models/Person.php

<?php

class Person extends Eloquent {

    protected $fillable = array('id', 'first_name', 'last_name');

    public $timestamps = false;

}

And finally, we have modified the app/routes.php and app/views/hello.php files in the following manner.

app/routes.php


...
Route::get('/', function()
{
    $people = Person::all();
    return View::make('hello', array('people' => $people));

});
...

app/views/hello.php


...
    <?php foreach($people as $person): ?>
    <p><?= "{$person->id} - {$person->first_name} {$person->last_name}" ?></p>
    <?php endforeach; ?>
</body>
...

unix_socket

Once that the last updates had been done, we have uploaded our application to App Engine and tried to access the application url.

The application returned an exception that indicates that the connection to the database has not succeeded for the following reason.

exception 'PDOException' with message 'SQLSTATE[HY000] [2002] Unable to find the socket transport "unix" - did you forget to enable it when you configured PHP?

From our analysis, it seems there is a bug in the Illuminate\Database\Connectors\MySqlConnector class of the Laravel 4 framework.

We propose a corrected version of this class that you will find in the gist https://gist.github.com/gmergoil/5693102 by which you must replace the content of the vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php file.

Note : during an update of the Laravel framework by using the composer update command, the modified MySqlConnector.php file will be reset to its default content. Thus, it will be necessary to update again the MySqlConnector.php file after any framework update and before any upload to App Engine. As it seems to be a bug in the framework, we will submit a fix to the Laravel team so that the bug is fixed in the future releases.

You can now upload your application to Google App Engine and you should get the following result.

laravel4-hello-modified-72dpi

Sumary of the experimentation

This experimentation allowed us to deploy a Laravel 4 standard application to the Google App Engine PHP runtime.

In order to succeed, we have had to modify the default configuration of App Engine and Laravel, and even update some Laravel internal components.

Here is the different steps we have followed :

  • add the app.yaml file,
  • add the gethostname() function,
  • authorize the php_sapi_name() and php_uname() functions in the php.ini file,
  • replace the Illuminate\Encryption Laravel package which uses the mcrypt PHP extension unavailable in the App Engine PHP runtime by the neoxia\laravel-openssl-encryption package which uses the openssl PHP extension,
  • replace the Laravel 4 default log manager by SyslogHandler handler included in the Monolog package,
  • activate the Laravel 4 session and cache manager based on Memcached,
  • regenerate the compiled.php file,
  • configure a connection to a Google Cloud SQL database,
  • replace the Illuminate\Database\Connectors\MySqlConnector class to fix a bug in the framework.

Conclusions

The main conclusion we can draw from this experimentation is that, used together, Google App Engine for PHP and Laravel 4 present a strong potential.

Laravel 4 is certainly the PHP framework that progresses the most at the moment, given its ease of use and the quality of its architecture and its code.

Google App Engine is one of the most powerful and accessible platforms PaaS, however with some restrictions. The support of the PHP language, announced at Google I/O 2013 is probably going to strongly increase the adoption of this platform.

The strength of Laravel combined 4 to those of Google App Engine gives a very effective solution to quickly produce high-level and highly scalable web applications.

Nevertheless, there are some steps before the integration of these two technologies reaches the level expected for a real production grade platform.

Firstly, the PHP support on the App Engine platform must improve and be aligned with standards concerning the available PHP extensions. During this testing, we have faced the lack of support of the PHP mcrypt extension for which we have found a work around. It has to be known that the App Engine PHP runtime does not support either the PHP mbstring extension which is widely used in PHP applications.
In the case of the Laravel 4 framework, the lack of the mbstring extension does not prevent the execution of the application because the framework substitutes its own PHP implementation of the mbstring functions, but their performances are far below the ones of the native functions.

Secondly, we have succeeded in configuring and slightly modifying the Laravel 4 framework in order to make it run on the App Engine PHP runtime. Nevertheless, a lot of side functions of the framework cannot be used at this time. For instance, it is the case of the migrations that have to be done by hand and without the use of the artisan tool. Also, it would be desirable if the Google App Engine platform meets success with the Laravel developers, that the community provides a version of its test and development tools that are tailored for the App Engine platform.

As a conclusion, we invite you to continue experimenting with these two promising technologies, but we advise you to wait a couple of month before considering using them for critical applications in production environments. In the meantime, Google App Engine will be out of his beta and will have moved towards more compliance with the PHP standards and Laravel could be equipped with tools specific to the App Engine platform … the Neoxia team is already working on it !

We would be very interested in your own experiments with Google App Engine for PHP and Laravel. Let us know through a comment of this blog.

References