🐘 ☁️ Serverless Computing with TuskLang & PHP
☁️ Serverless Computing with TuskLang & PHP
Introduction
Serverless computing is the future of scalable, cost-effective applications. TuskLang and PHP let you build sophisticated serverless functions with config-driven deployment, event handling, and optimization that scales automatically.Key Features
- AWS Lambda integration - Azure Functions support - Google Cloud Functions - Event-driven architecture - Cold start optimization - Function composition - Monitoring and loggingExample: Serverless Configuration
[serverless]
provider: aws
region: @env("AWS_REGION", "us-east-1")
runtime: @env("PHP_RUNTIME", "provided.al2")
memory: @env("LAMBDA_MEMORY", 512)
timeout: @env("LAMBDA_TIMEOUT", 30)
cold_start: @go("serverless.OptimizeColdStart")
metrics: @metrics("function_invocations", 0)
PHP: AWS Lambda Handler
<?phpnamespace App\Serverless;
use TuskLang\Config;
use TuskLang\Operators\Env;
use TuskLang\Operators\Metrics;
use TuskLang\Operators\Go;
class LambdaHandler
{
private $config;
private $router;
public function __construct()
{
$this->config = new Config();
$this->router = new FunctionRouter();
}
public function handle($event, $context)
{
try {
// Record invocation
Metrics::record("function_invocations", 1, [
"function_name" => $context->functionName,
"memory_limit" => $context->memoryLimitInMB
]);
// Parse event
$eventType = $this->getEventType($event);
$handler = $this->router->getHandler($eventType);
// Execute handler
$result = $handler->handle($event, $context);
// Record success
Metrics::record("function_success", 1, [
"function_name" => $context->functionName,
"event_type" => $eventType
]);
return $result;
} catch (\Exception $e) {
// Record error
Metrics::record("function_errors", 1, [
"function_name" => $context->functionName,
"error_type" => get_class($e)
]);
throw $e;
}
}
private function getEventType($event)
{
if (isset($event['Records'][0]['eventSource'])) {
return 'dynamodb';
}
if (isset($event['Records'][0]['s3'])) {
return 's3';
}
if (isset($event['Records'][0]['Sns'])) {
return 'sns';
}
if (isset($event['httpMethod'])) {
return 'api_gateway';
}
return 'custom';
}
}
class FunctionRouter
{
private $config;
private $handlers = [];
public function __construct()
{
$this->config = new Config();
$this->loadHandlers();
}
public function getHandler($eventType)
{
if (!isset($this->handlers[$eventType])) {
throw new \Exception("No handler found for event type: {$eventType}");
}
return $this->handlers[$eventType];
}
private function loadHandlers()
{
$handlerConfigs = $this->config->get("serverless.handlers", []);
foreach ($handlerConfigs as $type => $config) {
$this->handlers[$type] = new $config['class']();
}
}
}
Event-Driven Architecture
<?phpnamespace App\Serverless\Events;
use TuskLang\Config;
use TuskLang\Operators\Cache;
class EventProcessor
{
private $config;
private $processors = [];
public function __construct()
{
$this->config = new Config();
$this->loadProcessors();
}
public function process($event)
{
$eventType = $event['type'] ?? 'unknown';
$processor = $this->getProcessor($eventType);
return $processor->process($event);
}
private function getProcessor($eventType)
{
if (!isset($this->processors[$eventType])) {
throw new \Exception("No processor found for event type: {$eventType}");
}
return $this->processors[$eventType];
}
private function loadProcessors()
{
$processorConfigs = $this->config->get("serverless.event_processors", []);
foreach ($processorConfigs as $type => $config) {
$this->processors[$type] = new $config['class']();
}
}
}
class S3EventProcessor
{
public function process($event)
{
$records = $event['Records'] ?? [];
foreach ($records as $record) {
$bucket = $record['s3']['bucket']['name'];
$key = $record['s3']['object']['key'];
$eventName = $record['eventName'];
switch ($eventName) {
case 'ObjectCreated:Put':
$this->handleObjectCreated($bucket, $key);
break;
case 'ObjectRemoved:Delete':
$this->handleObjectDeleted($bucket, $key);
break;
}
}
}
private function handleObjectCreated($bucket, $key)
{
// Process new object
$s3Client = new S3Client();
$object = $s3Client->getObject([
'Bucket' => $bucket,
'Key' => $key
]);
// Process based on file type
$extension = pathinfo($key, PATHINFO_EXTENSION);
switch ($extension) {
case 'jpg':
case 'png':
$this->processImage($object);
break;
case 'json':
$this->processJson($object);
break;
case 'csv':
$this->processCsv($object);
break;
}
}
private function handleObjectDeleted($bucket, $key)
{
// Clean up related resources
$this->cleanupResources($bucket, $key);
}
}
class DynamoDBEventProcessor
{
public function process($event)
{
$records = $event['Records'] ?? [];
foreach ($records as $record) {
$eventName = $record['eventName'];
$dynamodb = $record['dynamodb'];
switch ($eventName) {
case 'INSERT':
$this->handleInsert($dynamodb);
break;
case 'MODIFY':
$this->handleModify($dynamodb);
break;
case 'REMOVE':
$this->handleRemove($dynamodb);
break;
}
}
}
private function handleInsert($dynamodb)
{
$newImage = $dynamodb['NewImage'] ?? [];
// Process new record
$this->processNewRecord($newImage);
}
private function handleModify($dynamodb)
{
$oldImage = $dynamodb['OldImage'] ?? [];
$newImage = $dynamodb['NewImage'] ?? [];
// Process modified record
$this->processModifiedRecord($oldImage, $newImage);
}
private function handleRemove($dynamodb)
{
$oldImage = $dynamodb['OldImage'] ?? [];
// Process deleted record
$this->processDeletedRecord($oldImage);
}
}
Cold Start Optimization
<?phpnamespace App\Serverless\Optimization;
use TuskLang\Config;
use TuskLang\Operators\Cache;
class ColdStartOptimizer
{
private $config;
private $cache;
public function __construct()
{
$this->config = new Config();
$this->cache = new Cache();
}
public function optimize()
{
// Pre-warm connections
$this->preWarmConnections();
// Pre-load configurations
$this->preLoadConfigurations();
// Pre-compile templates
$this->preCompileTemplates();
// Pre-load dependencies
$this->preLoadDependencies();
}
private function preWarmConnections()
{
$connections = $this->config->get("serverless.connections", []);
foreach ($connections as $type => $config) {
switch ($type) {
case 'database':
$this->preWarmDatabase($config);
break;
case 'redis':
$this->preWarmRedis($config);
break;
case 's3':
$this->preWarmS3($config);
break;
}
}
}
private function preWarmDatabase($config)
{
$dsn = $config['dsn'];
$pdo = new PDO($dsn);
// Execute a simple query to establish connection
$pdo->query("SELECT 1");
// Store connection in cache
$this->cache->set("db_connection", $pdo);
}
private function preWarmRedis($config)
{
$redis = new Redis();
$redis->connect($config['host'], $config['port']);
// Test connection
$redis->ping();
// Store connection in cache
$this->cache->set("redis_connection", $redis);
}
private function preWarmS3($config)
{
$s3Client = new S3Client([
'region' => $config['region'],
'version' => 'latest'
]);
// Store client in cache
$this->cache->set("s3_client", $s3Client);
}
private function preLoadConfigurations()
{
$configs = $this->config->get("serverless.preload_configs", []);
foreach ($configs as $configKey) {
$config = $this->config->get($configKey);
$this->cache->set("config_{$configKey}", $config);
}
}
private function preCompileTemplates()
{
$templates = $this->config->get("serverless.templates", []);
foreach ($templates as $template) {
$compiled = $this->compileTemplate($template);
$this->cache->set("template_{$template}", $compiled);
}
}
private function preLoadDependencies()
{
$dependencies = $this->config->get("serverless.dependencies", []);
foreach ($dependencies as $dependency) {
$instance = new $dependency();
$this->cache->set("dependency_{$dependency}", $instance);
}
}
}
Function Composition
<?phpnamespace App\Serverless\Composition;
use TuskLang\Config;
class FunctionComposer
{
private $config;
private $lambda;
public function __construct()
{
$this->config = new Config();
$this->lambda = new LambdaClient();
}
public function compose($workflow)
{
$steps = $this->config->get("serverless.workflows.{$workflow}", []);
$results = [];
foreach ($steps as $step) {
$functionName = $step['function'];
$input = $this->prepareInput($step, $results);
$result = $this->invokeFunction($functionName, $input);
$results[$step['name']] = $result;
// Check if we should continue
if (!$this->shouldContinue($step, $result)) {
break;
}
}
return $results;
}
private function prepareInput($step, $previousResults)
{
$input = $step['input'] ?? [];
// Replace placeholders with previous results
foreach ($previousResults as $name => $result) {
$input = str_replace("{{$name}}", $result, $input);
}
return $input;
}
private function invokeFunction($functionName, $payload)
{
$result = $this->lambda->invoke([
'FunctionName' => $functionName,
'Payload' => json_encode($payload),
'InvocationType' => 'RequestResponse'
]);
return json_decode($result['Payload']->getContents(), true);
}
private function shouldContinue($step, $result)
{
$condition = $step['condition'] ?? null;
if (!$condition) {
return true;
}
return $this->evaluateCondition($condition, $result);
}
private function evaluateCondition($condition, $result)
{
// Simple condition evaluation
$operator = $condition['operator'];
$value = $condition['value'];
$field = $condition['field'];
$actualValue = $this->getNestedValue($result, $field);
switch ($operator) {
case 'equals':
return $actualValue == $value;
case 'not_equals':
return $actualValue != $value;
case 'greater_than':
return $actualValue > $value;
case 'less_than':
return $actualValue < $value;
default:
return true;
}
}
private function getNestedValue($array, $path)
{
$keys = explode('.', $path);
$value = $array;
foreach ($keys as $key) {
if (!isset($value[$key])) {
return null;
}
$value = $value[$key];
}
return $value;
}
}
Monitoring and Logging
<?phpnamespace App\Serverless\Monitoring;
use TuskLang\Config;
use TuskLang\Operators\Metrics;
class ServerlessMonitor
{
private $config;
private $cloudwatch;
public function __construct()
{
$this->config = new Config();
$this->cloudwatch = new CloudWatchClient();
}
public function recordMetrics($functionName, $metrics)
{
foreach ($metrics as $name => $value) {
$this->cloudwatch->putMetricData([
'Namespace' => 'Serverless/PHP',
'MetricData' => [
[
'MetricName' => $name,
'Value' => $value,
'Unit' => 'Count',
'Dimensions' => [
[
'Name' => 'FunctionName',
'Value' => $functionName
]
]
]
]
]);
}
}
public function logEvent($functionName, $event, $level = 'INFO')
{
$logData = [
'timestamp' => date('c'),
'function_name' => $functionName,
'level' => $level,
'event' => $event
];
// Send to CloudWatch Logs
$this->cloudwatch->putLogEvents([
'logGroupName' => "/aws/lambda/{$functionName}",
'logStreamName' => date('Y/m/d/H'),
'logEvents' => [
[
'timestamp' => time() * 1000,
'message' => json_encode($logData)
]
]
]);
}
public function trackPerformance($functionName, $startTime, $endTime)
{
$duration = ($endTime - $startTime) * 1000; // Convert to milliseconds
$this->recordMetrics($functionName, [
'Duration' => $duration,
'Invocations' => 1
]);
// Check for cold starts
if ($this->isColdStart()) {
$this->recordMetrics($functionName, [
'ColdStarts' => 1
]);
}
}
private function isColdStart()
{
// Check if this is the first invocation
return !isset($_ENV['AWS_LAMBDA_RUNTIME_API']);
}
}