Building Scalable PHP ApplicationsUsing Google’s App Engine
Mandy Waite google.com/+MandyWaite@tekgrrl
Amy Unruhgoogle.com/+AmyUnruh@amygdala
Scale on Demand
Analytics
Fast Access to Cloud-Scale Services
Global Availability
Focus on developing your
App
Why Cloud for PHP?
App Engine
Simple to Scale- AutoScale
Trivial to manage- Fully managed- No patches- 24x7 operation by Google SREs
Easy to develop- Free to start- Local dev environment- Service abstractions
Handling variable load
Volatile Demand Fluctuation Steady Demand Growth
Inefficiency
With App Engineonly pay for what you use
With App Enginescale with efficiency and reliability
Downtime
Inefficiency
App Engine’s Cloud Scale Services
Cloud SQL
...and more
PageSpeedCloud Datastore
Cloud Storage
Cron JobsMemcache
Why PHP?
Top feature request for App Engine
Real World Example
Converting joind.in to App Engine
PHP on App Engine:what’s the environment like?
github.com/GoogleCloudPlatform/appengine-php - 5.4.19http://php-minishell.appspot.com/phpinfo: Production phpinfo()
How do we start?
application: <your-app-name>version: unoruntime: phpapi_version: 1
handlers:- url: /inc static_dir: inc- url: /.* script: index.php
app.yamlappengine.google.com
What is the environment like?
google_app_engine.enable_functions = "php_sapi_name"
php.ini
How do we log?
How do we log?function write_log($level = 'error', $msg, $php_error = FALSE){ if ($this->_enabled === FALSE) { return FALSE; } $level = strtoupper($level); if ( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold)) { return FALSE; }
syslog($level == 'ERROR' ? LOG_ERR : LOG_INFO, $msg); return TRUE;}
How do we log?
Hmmm… now we have a database error
What about the database?
What about the database?
$db['default']['hostname'] = ":/cloudsql/<PROJECT>:<INSTANCE>";$db['default']['username'] = "joindin";$db['default']['password'] = "password";$db['default']['database'] = "joindin";$db['default']['dbdriver'] = "mysql";$db['default']['dbprefix'] = "";$db['default']['pconnect'] = TRUE;
How do we call APIs?
Recommended:
use App Engine’s URLFetch service via standard HTTP stream wrappers and functions like file_get_contents()
Calling APIs?
$opts = array( 'http' => array( 'method' => 'POST', 'header' => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-length: ".strlen($msg)."\r\n", 'content' => $msg));$ctx = stream_context_create($opts);
$resp = file_get_contents( 'http://api.defensio.com' . $loc, false, $ctx);
How do we cache?
Output Cachingfunction _write_cache($output) { $CI =& get_instance(); $path = $CI->config->item('cache_path'); $cache_path = ($path == '') ? BASEPATH . 'cache/' : $path; $uri = $CI->config->item('base_url') . $CI->config->item('index_page') . $CI->uri->uri_string(); $cache_path .= md5($uri);
$mc = new Memcache; $mc->set($cache_path, $output, $this->cache_expiration * 60); return;}
Can we upload files?
Uploads?
cloud.google.com/console
App Engine Access to Cloud Storage
$options = [ 'gs_bucket_name' => 'joindin' ];
$upload_url = CloudStorageTools::createUploadUrl( '/event/edit/'.$this->edit_id, $options);
echo form_open_multipart($upload_url);
Uploads?
Uploads?— and Cloud Storage stream wrapper
$gs_name = $_FILES[$field]['tmp_name'];$gs_type = $_FILES[$field]['type'];
$options = [ "gs" => [ "Content-Type" => $gs_type, "acl" => 'public-read']];$ctx = stream_context_create($options);
rename($gs_name, $this->upload_path . $this->file_name, $ctx);
Public URLs from Cloud Storage Files?
<?php $path = "gs://joindin/inc/img/event_icons/';$img = ( !empty($event->event_icon) && is_file($path.$event->event_icon)) ? escape($event->event_icon) : 'none.gif'; $public_url = CloudStorageTools::getPublicUrl($path.$img, true);?>
Can we send email?
Can we send email?
require_once 'google/appengine/api/mail/Message.php';use google\appengine\api\mail\Message;
... $message = new Message($mail_options); $message->send();
How can we send email in the background?
Web Request Worker
Task Queue
foreach ($send_to as $email) { // Send mail in a worker. $task = new PushTask('/tasks/email', [ 'from' => $from, 'to' => $email, 'subject' => $subj, 'msg' => $msg ]); // Use the default queue. $task_name = $task->add();}
Using Task Queues to send email in the background
The task handler mapping
handlers:- url: /inc static_dir: inc- url: /tasks/email script: workers/email.php login: admin
app.yaml
Can we send email? - the task handler
require_once 'google/appengine/api/mail/Message.php';use google\appengine\api\mail\Message;
if (!isset($_SERVER['HTTP_X_APPENGINE_QUEUENAME'])) { exit(); }...
Can we send email? - the task handler (cont.)
...$mail_options = [ "sender" => $_POST['from'], "to" => $_POST['to'], "subject" => $_POST['subject'], "htmlBody" => $_POST['msg']];
try { $message = new Message($mail_options); $message->send();} catch (\InvalidArgumentException $e) { syslog(LOG_ERR, $e->getMessage());}
Migrating a Typical Application
PHP
Use streams API for network calls
Cache to Memcache
Mail API to send email
Cron, Task Queues for background jobs
MySQL to Cloud SQL,or Cloud Datastore for NoSQL
File Writes and Uploads to Google Cloud Storage
Create App Engine application
Configureapp.yaml, php.ini
Apply at goo.gl/Bc1sA
Use Promo Code: fphpp-con
Google Cloud Platform Starter Pack allows developers from affiliated partners to receive $2,000 of credit - $1,000 for Google App Engine and $1,000 for Google Compute Engine and other Cloud Platform services
Google Cloud Platform Starter Pack
'Unofficial' GAE PHP tips BlogThe I/O session talkDocumentationCloud Platform BlogGetting started with other apps:WordPress WP starter project on GitHub with the App Engine WordPress pluginLaravelDrupal
Q & A… and Resources
Thank you!
</end>