I decided to write a mini MVC framework in PHP that I thought you may find interesting. You can read about it in this article.
Demo URL: https://0qdhumcwd5.execute-api.ap-southeast-2.amazonaws.com/Prod/
Introduction
Around a year ago, I put together an article on building your own custom runtime for lambda that enabled you to run PHP. Running PHP this way has a number of benefits, it will scale well, cost very little to host and if you are familiar with PHP and not AWS, then this could be a fun project to try out. Well, over the past year, several people have reached out regarding this and this weekend, I was helping someone get this to work. While remembering how to use PHP, I decided to tinker a little bit and write a mini MVC framework in PHP.
The final application can be seen live here.
Background
For some background, there are a number of good repos available here. Actually a lot of my sample repo is based of this, however if you don't have PHP setup already, this can be hard to deploy and figure out how to install all the dependencies and install composer. So in addition to the code, I added the instructions to setup and run this in Cloud9 as well as a short video.
Using the Code
If you are not using AWS, then SAM might be new to you, I will quickly introduce this. SAM is the Serverless Application Model, it helps you quickly build serverless infrastructure for AWS with a template yaml file. Let's take a quick look at this one:
Please note that I have hard coded the region for the layer I am using:
AWSTemplateFormatVersion: 2010-09-09
Description: Testing PHP and Lambda
Transform: AWS::Serverless-2016-10-31
##########################################################################
# Parameters & Globals #
##########################################################################
Globals:
Function:
Timeout: 3
Resources:
##########################################################################
# Lambda function with PHP runtime provided by layers #
##########################################################################
CatchAllLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Description: Lambda function to hosts entire application codebase
CodeUri: ./code/
Runtime: provided
Handler: index.php
MemorySize: 4096
Timeout: 30
Tracing: Active
Layers:
- 'arn:aws:lambda:ap-southeast-2:209497400698:layer:php-73-fpm:25'
Events:
DynamicRequestsRoot:
Type: Api
Properties:
Path: /
Method: ANY
DynamicRequestsProxy:
Type: Api
Properties:
Path: /{proxy+}
Method: ANY
##########################################################################
# Stack Outputs #
##########################################################################
Outputs:
WebEndpoint:
Description: "API Gateway endpoint URL for Prod stage"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
At a high level, this template specified the following:
- Deploy a lambda function
- Deploy an API Gateway, so we can invoke the PHP function in a browser.
- Attach a layer to the lambda function that is prebuilt to support PHP (a layer is additional dependencies required to run your code).
- Specify the code folder as the source for the code we will deploy, this can contain many files and folders if required.
- Force the entry point for all incoming requests to index.php.
- Allocate 4096MB of RAM to this function, and specify a max runtime of 3 seconds.
If we think about this file, it does a lot, it fully replaces building a server, installing PHP, installing Apache of nginx, configuring PHP and server hardening. This is a huge benefit and time saver.
Deployment
Rather then adding static images, I have opted to make a small video:
Create a new Cloud9 Environment, the smallest instance size will be just fine, but ensure you select Amazon Linux 2.
In Cloud 9, perform the following steps in the terminal:
sudo yum -y update
sudo amazon-linux-extras install -y php7.2 php-mbstring
git clone https:
cd aws-lambda-php-mvc
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
This installs PHP 7.2, clones the repo and installs a composer, next we will install bref
and guzzel
.
cd code
php ../composer.phar require bref/bref
php ../composer.phar require guzzlehttp/guzzle
Open the template.yaml, update line 27 to align the region you wish to deploy into. You can look here for available layers: https://runtimes.bref.sh/.
Then run:
cd ..
sam deploy --guided
Follow the instructions and ensure you deploy to the region you specified in the template.yaml.
Once that is deployed, you can open the WebEndpoint from the outputs section in your browser and you'll see the app running.
MVC Code
index.php
require_once 'vendor/autoload.php';
$__path = explode("/", $_SERVER['REQUEST_URI']);
if(count($__path) < 3){
$__controller = 'controller/base.php';
$__view = 'view/base/base.php';
$__controllerMethod = 'base';
}else{
$__pos = strpos($_SERVER['REQUEST_URI'], "/");
if ($__pos !== false) {
$__view = "view/" .substr_replace($_SERVER['REQUEST_URI'], "/", $__pos, 1) .".php";
$__controller = "controller/" .substr_replace($_SERVER['REQUEST_URI'], "/", $__pos, 1);
}
$__method_arr = explode ("/", $__controller);
$__controllerMethod = end($__method_arr);
array_pop($__method_arr);
$__controller = implode ($__method_arr, "/") .'.php';
}
include $__controller;
$_rc = call_user_func($__controllerMethod);
include $__view;
All this code does is basically parse out which two files to "include" and which method to run. $__rc
is used as the request context variable in which to pass information from the controller to the view. The controller will handle any business logic, com's to the database or external source and the view should contain only basic logic for display purposes.
random.php (sample controller)
function joke() {
$client = new GuzzleHttp\Client();
$res = $client->get('https://official-joke-api.appspot.com/random_joke');
$json = $res->getBody();
$result = json_decode($json);
$_rc = [
"httpResult" => $result
];
return $_rc;
}
joke.php (sample view)
<html>
<body>
<h1>This is the Random Joke page!.</h1>
<p>Enjoy the Joke Text:</p>
<div>Setup: <?php echo $_rc["httpResult"]->setup; ?></div>
<div>Punchline: <?php echo $_rc["httpResult"]->punchline; ?></div>
<pre>
<?php
krumo($_rc["httpResult"]);
?>
</pre>
<a href="/Prod">Home</a>
</body>
</html>
Krumo is included for basic debugging.
Repository: https://github.com/kukielp/aws-lambda-php-mvc
This was a fun little experiment and I hope you enjoyed reading it!
History
- 14th June, 2021: Initial version