A presentation at Sunshine PHP 2019 in in Miami, FL, USA by Rob Allen
Introduction to Serverless PHP Rob Allen February 2019 Slides: https://akrabat.com/5703
Platform options Rob Allen ~ @akrabat
Serverless Serverless is all about composing software systems from a collection of cloud services. With serverless, you can lean on off-the-shelf cloud services resources for your application architecture, focus on business logic and application needs. Nate Taggart, CEO Stackery Rob Allen ~ @akrabat
FaaS • • • • • Your code Deployed to the cloud Runs when needed Scaled automatically Pay only for execution Rob Allen ~ @akrabat
Where are the servers? Rob Allen ~ @akrabat
Rob Allen ~ @akrabat
Rob Allen ~ @akrabat
Use-cases Synchronous Service is invoked and provides immediate response (HTTP requests: APIs, chat bots) Asynchronous Push a message which drives an action later (web hooks, timed events, database changes) Rob Allen ~ @akrabat
Benefits • • • • No need to maintain infrastructure Concentrate on application code Pay only for what you use, when you use it Language agnostic Rob Allen ~ @akrabat
Challenges • • • • Start up latency Time limit State is external Different way of thinking Rob Allen ~ @akrabat
It’s about value Rob Allen ~ @akrabat
When should you use serverless? • • • • • Responding to web hooks PWA/Static site contact form, et al. Additional features without extending current platform Variable traffic levels When you want your costs to scale with traffic Rob Allen ~ @akrabat
Serverless platforms Rob Allen ~ @akrabat
Serverless platforms with PHP support Rob Allen ~ @akrabat
Concepts Rob Allen ~ @akrabat
Rob Allen ~ @akrabat
Rob Allen ~ @akrabat
Hello world in PHP Rob Allen ~ @akrabat
Hello world in PHP Rob Allen ~ @akrabat
Upload your action $ wsk action update hello hello.php ok: updated action hello Rob Allen ~ @akrabat
Run your action $ wsk action invoke hello —result { “msg”: “Hello World” } Rob Allen ~ @akrabat
Segue: How did it do this? Rob Allen ~ @akrabat
OpenWhisk’s architecture Rob Allen ~ @akrabat
Create an action Rob Allen ~ @akrabat
Invoke an action Rob Allen ~ @akrabat
Action container lifecycle • Hosts the user-written code • Controlled via two end points: /init & /run Rob Allen ~ @akrabat
Action container lifecycle • Hosts the user-written code • Controlled via two end points: /init & /run Rob Allen ~ @akrabat
End Segue Rob Allen ~ @akrabat
Public web access Add the —web flag: $ wsk action update hello hello.php —web true Rob Allen ~ @akrabat
Public web access Add the —web flag: $ wsk action update hello hello.php —web true $ curl https://openwhisk.ng.bluemix.net/api/v1/web/ \ 19FT_demo/default/hello.json { “msg”: “Hello World” } Rob Allen ~ @akrabat
API Gateway When you want to do more with HTTP endpoints • • • • • Route endpoint methods to actions (Open API Spec support) Custom domains Rate limiting Security (API keys, OAuth, CORS) Analytics Rob Allen ~ @akrabat
API Gateway $ wsk api create /myapp /hello GET hello ok: created API /myapp/hello GET for action /_/hello Rob Allen ~ @akrabat
Todo-Backend Rob Allen ~ @akrabat
Todo-Backend Add, edit and delete to-do items using OpenWhisk (on IBM) with PostgreSQL Rob Allen ~ @akrabat
Serverless Framework application manifest: serverless.yml service: ow-todo-backend provider: name: openwhisk runtime: php plugins: - serverless-openwhisk Rob Allen ~ @akrabat
Configure action functions: edit-todo: handler: “src/actions/editTodo.main” name: “todo-backend/edit-todo” Rob Allen ~ @akrabat
Configure action functions: edit-todo: handler: “src/actions/editTodo.main” name: “todo-backend/edit-todo” Rob Allen ~ @akrabat
Configure action functions: edit-todo: handler: “src/actions/editTodo.main” name: “todo-backend/edit-todo” Rob Allen ~ @akrabat
Configure API Gateway functions: edit-todo: handler: “src/actions/editTodo.main” name: “todo-backend/edit-todo” events: - http: path: /todos/{id} method: patch Rob Allen ~ @akrabat
Configure API Gateway functions: edit-todo: handler: “src/actions/editTodo.main” name: “todo-backend/edit-todo” events: - http: path: /todos/{id} method: patch Rob Allen ~ @akrabat
Project files . ├── ├── ├── ├── ├── src/ vendor/ composer.json composer.lock serverless.yml src/ ├── Todo/ │ ├── Todo.php │ ├── TodoMapper.php │ └── TodoTransformer.php ├── actions/ │ ├── addTodo.php │ ├── deleteTodo.php │ ├── editTodo.php │ ├── listTodos.php │ └── showTodo.php └── AppContainer.php Rob Allen ~ @akrabat
editTodo.php function main(array $args) : array { try { $parts = explode(“/”, $args[‘__ow_path’]); $id = (int)array_pop($parts); $data = json_decode(base64_decode($args[‘__ow_body’]), true); if (!is_array($data)) { throw new InvalidArgumentException(‘Missing body’, 400); } $container = new AppContainer($args); $mapper = $container[TodoMapper::class]; $todo = $mapper->loadById($id); $mapper->update($todo, $data); $transformer = $container[TodoTransformer::class]; $resource = new Item($todo, $transformer, ‘todos’); $fractal = $container[Manager::class]; return [ ‘statusCode’ => 200, ‘body’ => $fractal->createData($resource)->toArray(), ]; } catch (\Throwable $e) { var_dump((string)$e); $code = $e->getCode() < 400 ? $e->getCode(): 500; return [ ‘statusCode’ => $code, ‘body’ => [‘error’ => $e->getMessage()]]; } } Rob Allen ~ @akrabat
editTodo.php: Error handling function main(array $args) : array { try { // do stuff } catch (\Throwable $e) { var_dump((string)$e); $code = $e->getCode() < 400 ? $e->getCode() : 500; return [ ‘statusCode’ => $code, ‘body’ => [‘error’ => $e->getMessage()]]; } } Rob Allen ~ @akrabat
editTodo.php: Error handling function main(array $args) : array { try { // do stuff } catch (\Throwable $e) { var_dump((string)$e); $code = $e->getCode() < 400 ? $e->getCode() : 500; return [ ‘statusCode’ => $code, ‘body’ => [‘error’ => $e->getMessage()]]; } } Rob Allen ~ @akrabat
editTodo.php: Error handling function main(array $args) : array { try { // do stuff } catch (\Throwable $e) { var_dump((string)$e); $code = $e->getCode() < 400 ? $e->getCode() : 500; return [ ‘statusCode’ => $code, ‘body’ => [‘error’ => $e->getMessage()]]; } } Rob Allen ~ @akrabat
editTodo.php: Error handling function main(array $args) : array { try { // do stuff } catch (\Throwable $e) { var_dump((string)$e); $code = $e->getCode() < 400 ? $e->getCode() : 500; return [ ‘statusCode’ => $code, ‘body’ => [‘error’ => $e->getMessage()]]; } } Rob Allen ~ @akrabat
editTodo.php: Grab input $parts = explode(“/”, $args[‘__ow_path’]); $id = (int)array_pop($parts); Rob Allen ~ @akrabat
editTodo.php: Grab input $parts = explode(“/”, $args[‘__ow_path’]); $id = (int)array_pop($parts); $body = base64_decode($args[‘__ow_body’]; $data = json_decode($body), true); if (!is_array($data)) { throw new Exception(‘Missing body’, 400); } Rob Allen ~ @akrabat
editTodo.php: Grab input $parts = explode(“/”, $args[‘__ow_path’]); $id = (int)array_pop($parts); $body = base64_decode($args[‘__ow_body’]; $data = json_decode($body), true); if (!is_array($data)) { throw new Exception(‘Missing body’, 400); } Rob Allen ~ @akrabat
editTodo.php: Do the work $container = new AppContainer($args); $mapper = $container[TodoMapper::class]; $todo = $mapper->loadById($id); $mapper->update($todo, $data); Rob Allen ~ @akrabat
editTodo.php: Do the work $container = new AppContainer($args); $mapper = $container[TodoMapper::class]; $todo = $mapper->loadById($id); $mapper->update($todo, $data); Rob Allen ~ @akrabat
editTodo.php: Do the work $container = new AppContainer($args); $mapper = $container[TodoMapper::class]; $todo = $mapper->loadById($id); $mapper->update($todo, $data); Rob Allen ~ @akrabat
editTodo.php: Present results $transformer = $container[TodoTransformer::class]; $resource = new Item($todo, $transformer, ‘todos’); $fractal = $container[Manager::class]; $output = $fractal->createData($resource); return [ ‘statusCode’ => 200, ‘body’ => $output->toArray(), ]; Rob Allen ~ @akrabat
editTodo.php: Present results $transformer = $container[TodoTransformer::class]; $resource = new Item($todo, $transformer, ‘todos’); $fractal = $container[Manager::class]; $output = $fractal->createData($resource); return [ ‘statusCode’ => 200, ‘body’ => $output->toArray(), ]; Rob Allen ~ @akrabat
editTodo.php: Present results $transformer = $container[TodoTransformer::class]; $resource = new Item($todo, $transformer, ‘todos’); $fractal = $container[Manager::class]; $output = $fractal->createData($resource); return [ ‘statusCode’ => 200, ‘body’ => $output->toArray(), ]; Rob Allen ~ @akrabat
editTodo.php: Present results $transformer = $container[TodoTransformer::class]; $resource = new Item($todo, $transformer, ‘todos’); $fractal = $container[Manager::class]; $output = $fractal->createData($resource); return [ ‘statusCode’ => 200, ‘body’ => $output->toArray(), ]; Rob Allen ~ @akrabat
Deploy $ serverless deploy Serverless: Packaging Serverless: Compiling Serverless: Compiling Serverless: Compiling Serverless: Compiling Serverless: Compiling Serverless: Compiling Serverless: Deploying Serverless: Deploying Serverless: Deploying […] service… Functions… Packages… API Gateway definitions… Rules… Triggers & Feeds… Service Bindings… Packages… Functions… API Gateway definitions… Rob Allen ~ @akrabat
A working API! Rob Allen ~ @akrabat
Rob Allen ~ @akrabat
AWS Lambda with PHP Only sensibly possible since November 2018 with the introduction of layers Process: 1. Create a layer containing: 1. the PHP executable 2. a bootstrap script 2. Write the PHP function! Full details: akrabat.com/lambdaphp Rob Allen ~ @akrabat
Bootstrap file while (true) { // get next event $eventPayload = $lambdaRuntime->getEventPayload(); // execute handler $data = $function($eventPayload); // send result $lambdaRuntime->sendResult($data); } Rob Allen ~ @akrabat
Manage using Serverless Framework service: hello-lambdaphp provider: name: aws runtime: provided memorySize: 128 layers: php: path: layer/php Rob Allen ~ @akrabat
Hello World in Lambda function hello($eventData) { $data = json_decode($eventData, true, 512, JSON_THROW_ON_ERROR); $name = $data[‘name’] ?? ‘World’; return json_encode([‘msg’ => “Hello $name”]); } Rob Allen ~ @akrabat
Manage using Serverless Framework functions: hello: handler: handler.hello layers: - {Ref: PhpLambdaLayer} Rob Allen ~ @akrabat
Deploy using Serverless Framework $ sls deploy Serverless: Packaging service… Serverless: Uploading CloudFormation file to S3… … functions: hello: hello-lambdaphp-dev-hello layers: php: arn:aws:lambda:eu-west-2:661969457706:layer:php: Serverless: Removing old service artifacts from S3… Rob Allen ~ @akrabat
Invoke using Serverless Framework $ sls invoke -f hello —data=’{“name”:”Rob”}’ { “msg”: “Hello Rob” } Rob Allen ~ @akrabat
Bref Community maintained PHP runtime for AWS Lambda <?php require DIR.’/vendor/autoload.php’; lambda(function (array $event) { $name = $event[‘name’] ?? ‘World’; return [‘msg’ => “Hello $name”]; }); Rob Allen ~ @akrabat
Project 365 My photo-a-day website Rob Allen ~ @akrabat
Project 365 Static website to display my photo-a-day picture for each day of the year. • Hosted on S3 • CloudFront CDN • Lambda/PHP function Rob Allen ~ @akrabat
Lambda/PHP function Rob Allen ~ @akrabat
Serverless configuration functions: update: handler: src/actions/update.main layers: - {Ref: PhpLambdaLayer} environment: FLICKR_API_KEY: ${self:custom.FLICKR_API_KEY} FLICKR_USER_ID: ${self:custom.FLICKR_USER_ID} events: - schedule: name: project365-build rate: cron(0 */2 * * ? *) Rob Allen ~ @akrabat
main() function main(array $eventData) : array { $apiKey = getEnvVar(‘P365_FLICKR_API_KEY’); $userId = getEnvVar(‘P365_FLICKR_USER_ID’); $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator($apiKey); $html = $pageCreator->update($year, $userId); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat
main() function main(array $eventData) : array { $apiKey = getEnvVar(‘P365_FLICKR_API_KEY’); $userId = getEnvVar(‘P365_FLICKR_USER_ID’); $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator($apiKey); $html = $pageCreator->update($year, $userId); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat
main() function main(array $eventData) : array { $apiKey = getEnvVar(‘P365_FLICKR_API_KEY’); $userId = getEnvVar(‘P365_FLICKR_USER_ID’); $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator($apiKey); $html = $pageCreator->update($year, $userId); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat
main() function main(array $eventData) : array { $apiKey = getEnvVar(‘P365_FLICKR_API_KEY’); $userId = getEnvVar(‘P365_FLICKR_USER_ID’); $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator($apiKey); $html = $pageCreator->update($year, $userId); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat
main() function main(array $eventData) : array { $apiKey = getEnvVar(‘P365_FLICKR_API_KEY’); $userId = getEnvVar(‘P365_FLICKR_USER_ID’); $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator($apiKey); $html = $pageCreator->update($year, $userId); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat
Fetch photos from Flickr $url = ‘?’ . http_build_query([ ‘api_key’ => $this->flickrApiKey, ‘user_id’ => $flickrUserId, ‘extras’ => ‘url_z, date_taken, owner_name’, ‘method’ => ‘flickr.photos.search’, ‘tags’ => $year, ]); $response = $this->client->get($url); $data = json_decode($response->getBody(), true); return $data[‘photos’]; Rob Allen ~ @akrabat
Fetch photos from Flickr $url = ‘?’ . http_build_query([ ‘api_key’ => $this->flickrApiKey, ‘user_id’ => $flickrUserId, ‘extras’ => ‘url_z, date_taken, owner_name’, ‘method’ => ‘flickr.photos.search’, ‘tags’ => $year, ]); $response = $this->client->get($url); $data = json_decode($response->getBody(), true); return $data[‘photos’]; Rob Allen ~ @akrabat
Fetch photos from Flickr $url = ‘?’ . http_build_query([ ‘api_key’ => $this->flickrApiKey, ‘user_id’ => $flickrUserId, ‘extras’ => ‘url_z, date_taken, owner_name’, ‘method’ => ‘flickr.photos.search’, ‘tags’ => $year, ]); $response = $this->client->get($url); $data = json_decode($response->getBody(), true); return $data[‘photos’]; Rob Allen ~ @akrabat
Upload to S3 $s3 = new S3Client([ ‘version’ => ‘latest’, ‘region’ => getenv(‘AWS_DEFAULT_REGION’) ]); $s3->putObject([ ‘Bucket’ => $bucketName, ‘ACL’ => ‘public-read’, ‘Key’ => “/$year.html”, ‘Body’ => $data, ‘ContentType’ => ‘text/html’, ]); Rob Allen ~ @akrabat
Upload to S3 $s3 = new S3Client([ ‘version’ => ‘latest’, ‘region’ => getenv(‘AWS_DEFAULT_REGION’) ]); $s3->putObject([ ‘Bucket’ => $bucketName, ‘ACL’ => ‘public-read’, ‘Key’ => “/$year.html”, ‘Body’ => $data, ‘ContentType’ => ‘text/html’, ]); Rob Allen ~ @akrabat
Upload to S3 $s3 = new S3Client([ ‘version’ => ‘latest’, ‘region’ => getenv(‘AWS_DEFAULT_REGION’) ]); $s3->putObject([ ‘Bucket’ => $bucketName, ‘ACL’ => ‘public-read’, ‘Key’ => “/$year.html”, ‘Body’ => $data, ‘ContentType’ => ‘text/html’, ]); Rob Allen ~ @akrabat
Invalidate CloudFront $cft = new CloudFrontClient([ .. ]); $result = $cft->createInvalidation([ ‘DistributionId’ => $cloudFrontId, ‘InvalidationBatch’ => [ ‘CallerReference’ => date(‘YmdHis’), ‘Paths’ => [ ‘Items’ => [“/$year.html”], ‘Quantity’ => 1, ], ], ]); Rob Allen ~ @akrabat
Invalidate CloudFront $cft = new CloudFrontClient([ .. ]); $result = $cft->createInvalidation([ ‘DistributionId’ => $cloudFrontId, ‘InvalidationBatch’ => [ ‘CallerReference’ => date(‘YmdHis’), ‘Paths’ => [ ‘Items’ => [“/$year.html”], ‘Quantity’ => 1, ], ], ]); Rob Allen ~ @akrabat
Invalidate CloudFront $cft = new CloudFrontClient([ .. ]); $result = $cft->createInvalidation([ ‘DistributionId’ => $cloudFrontId, ‘InvalidationBatch’ => [ ‘CallerReference’ => date(‘YmdHis’), ‘Paths’ => [ ‘Items’ => [“/$year.html”], ‘Quantity’ => 1, ], ], ]); Rob Allen ~ @akrabat
Invalidate CloudFront $cft = new CloudFrontClient([ .. ]); $result = $cft->createInvalidation([ ‘DistributionId’ => $cloudFrontId, ‘InvalidationBatch’ => [ ‘CallerReference’ => date(‘YmdHis’), ‘Paths’ => [ ‘Items’ => [“/$year.html”], ‘Quantity’ => 1, ], ], ]); Rob Allen ~ @akrabat
The finished website Rob Allen ~ @akrabat
To sum up Rob Allen ~ @akrabat
Resources • https://akrabat.com • https://www.martinfowler.com/articles/serverless.html • https://github.com/akrabat/ow-php-todo-backend • https://github.com/akrabat/project365-photos-website • http://www.openwhisk.org • https://aws.amazon.com/lambda/ • https://bref.sh Rob Allen ~ @akrabat
Thank you! https://joind.in/talk/e6b2a Rob Allen ~ @akrabat
APIs and microservices are how we build modern web applications and serverless technologies make this easy. This session will show you how serverless applications are built and how you can leverage your PHP skills to build APIs of all shapes and sizes. We will cover how to use your current knowledge to build applications in PHP within Apache OpenWhisk or AWS Lambda, leveraging the API Gateway to build robust APIs quickly and easily. By the end of the session, you’ll be well placed to design and build your own microservices that take full advantage of the power of serverless technologies.
The following resources were mentioned during the presentation or are useful additional information.
Overview on serverless computing by Mike Roberts
A Todo-Backend implementation in OpenWhisk PHP
Photo-a-day static CloudFront/S3 website created by Lambda/PHP
Home page for the OpenWhisk project
Home page for AWS Lambda
Matthieu Napoli’s excellent toolkit to write serverless PHP applications on AWS Lambda
Here’s what was said about this presentation on social media.
Serverless PHP talk by @akrabat at @SunShinePHP conference #SunPHP19 pic.twitter.com/OxJjAmGBpo
— Enrico Zimuel (@ezimuel) February 8, 2019
It's @akrabat talking about #Serverless at #SunPHP19. I hope you're all paying attention, I've got a little bit of serverless in my webhooks talk tomorrow but nothing like the detail we're getting here! pic.twitter.com/RGIhPRoEtX
— Lorna Mitchell (@lornajane) February 8, 2019
New obsession: serverless! Thank you @akrabat and @amritajain973 for the talks! #SunPHP19
— Will Lewis (@jwlewisiii) February 8, 2019
Checking out “Introductions to serverless PHP” from @akrabat at #SunPHP19
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Starting with the basis of Serverless, you increase abstraction and simplicity of execution by trading control.
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
The servers still exist, but they are not your problem anymore. @akrabat #SunPHP19 pic.twitter.com/j1TNo34GzW
“I like not worrying about infrastructure because I don’t have to worry about Ops” @akrabat #SunPHP19
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
[At least we didn’t say NoOps? Deploying services as functions should still care about operations, it looks different, but you’re still interconnecting systems, employ experts]
Challenges of Serverless:
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Different way of thinking about applications
Start up latency
Time limited
State exists externally. @akrabat #SunPHP19
The abstraction is consistent with modern technology. A journalist doesn’t have to understand a printing press, but cares about the cost of printing.
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
App author doesn’t need to know the infra, but pays the cost of executing. @akrabat #SunPHP19
Serverless platforms that support PHP:
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
IBM
Open Whisk
Open PaaS
AWS (now, it took a bit to get to a logical place)@akrabat #SunPHP19
We build our functions with an entry point, event parameters and a service result.
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Standard upload and invoke process in Whisk.
It’s OSS though, so we can see how it works under the hood. @akrabat #SunPHP19 pic.twitter.com/6V5VM2YMxh
Using the API gateway of the Serverless host means you don’t have to build out your complicated routing. It’s not the interesting part of your app so offload itnonto your provider and commoditize it. @akrabat #SunPHP19
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Don’t use the web consoles. You can only do it the same way once. Use infra-code to ensure repeatable operations.
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Don’t put all your functions in a flat file. You’re still maintaining an application and good practices apply. @akrabat #SunPHP19 pic.twitter.com/9F9uY15Gho
Functions default output to the logs unless I specify a return. This lets me dump to logs easily while returning only relevant info to the end user. @akrabat #SunPHP19
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Next step : Introduction to Serverless PHP by Rob Allen ! #SunPHP19 pic.twitter.com/FbUUaw4laN
— Maxime Huran 🌈 (@MaximeHuran) February 8, 2019
@akrabat is talking about Serverless PHP at #SunPHP19 pic.twitter.com/JL6JpjONcj
— Peter Fisher (@pfwd) February 8, 2019
Things change in AWS recently. PHP only works sensibly now that AWS includes Layers.
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
We need a layer with the PHP binary and bootstrapping to float the PHP function on.
More details: https://t.co/2VP6Fhx1Yi@akrabat #SunPHP19
More infra code for building your functions in Lambda. This misses the bootstrapper (which is largely an application that sits and waits for inputs, check the link from before for more info) @akrabat #SunPHP19 pic.twitter.com/YVbmN7VwrB
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Also here’s a community tool for running in Amazon so you don’t necessarily have to rebuild a PHP runtime yourself. @akrabat #SunPHP19 pic.twitter.com/G2wHdk4jAr
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Sum up: Serverless is here and is going to be the new way to operate. When it’s a good fit it saves effort and money and you’ll all have to use it in ten years. @akrabat #SunPHP19
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Missed the resource slide, but @akrabat gave a great overview and will tweet it his slides later if you’re interested. #SunPHP19 Thanks!
— aaron aldrich @ SunshinePHP (@crayzeigh) February 8, 2019
Listening to @akrabat talk about serverless #PHP at #SunPHP19. pic.twitter.com/AmDSA08FDf
— David Bisset @ SunshinePHP 2019 (@dimensionmedia) February 8, 2019
Now @akrabat talking about #serverless #applications. Awesome! *** Day 1 at #SunPHP19 *** pic.twitter.com/tUCygGCAgX
— Solcre Technology Solutions (@solcre) February 8, 2019
Interesting stuff: @akrabat talking about serverless #PHP on AWS Lambda with “Bref” https://t.co/G2lbiRa5hY #SunPHP19
— David Bisset @ SunshinePHP 2019 (@dimensionmedia) February 8, 2019
.@akrabat showing how he uses his own serverless PHP API to grab daily photos from @Flickr and adds them to Amazon S3. This man lives in the future. #SunshinePHP
— David Bisset @ SunshinePHP 2019 (@dimensionmedia) February 8, 2019
“You will see more serverless PHP in the future. By 2022 you’ll likely be writing serverless PHP as part of your framework. Play with it now.” @akrabat #SunshinePHP pic.twitter.com/TLLJh0dQMQ
— David Bisset @ SunshinePHP 2019 (@dimensionmedia) February 8, 2019
"Devops is a thing. I like the dev, not the ops" —@akrabat with a very relatable quote on the benefits of Serverless PHP #SunPHP19
— Bill Yanelli (@billisonline) February 8, 2019
@akrabat I truly enjoyed your talk on Serverless #PHP. You communicated your thoughts in a clear and concise way. I learned new stuff and reviewed some that I already knew. Thanks. #SunPHP19
— Ravi Gehlot (@ravigehlot) February 8, 2019