Skip to content

Instantly share code, notes, and snippets.

@igorw

igorw/README.md Secret

Last active January 5, 2021 22:12
Show Gist options
  • Star 79 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save igorw/1d67f422689017e814a8 to your computer and use it in GitHub Desktop.
Save igorw/1d67f422689017e814a8 to your computer and use it in GitHub Desktop.
api-api, an api for creating apis, coming to a conference near you ... blame amanda for this

API API

a docker-inspired heroku clone in 100 lines of php

  • php supervisor.php
  • php -S 127.0.0.1:8080 app.php
  • git archive --format=zip master > api-api.zip
  • curl -XPOST 127.0.0.1:8080/api/api -F file='@api-api.zip'
<?php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Process\Process;
require 'vendor/autoload.php';
$app = new Silex\Application();
$app['debug'] = true;
$app->get('/', function () {
return <<<HTML
<!DOCTYPE html>
<html>
<head>
<title>API API</title>
</head>
<body>
<h1>API API</h1>
<p>This is an API for creating APIs. Just send a
<code>POST</code> request to /api/api with a zip
file attached containing your code under the form
key <code>file</code>.</p>
<p>We will then run your API!</p>
</body>
</html>
HTML;
});
$app->post('/api/api', function (Request $request) use ($app) {
$appId = hash('sha1', randomBytes(512));
$appDir = __DIR__.'/tmp/'.$appId;
if (!$request->files->has('file')) {
return $app->json(['error' => 'missing file'], 412);
}
if (!is_dir($appDir)) {
mkdir($appDir, 0755, true);
}
$file = $request->files->get('file');
$file->move($appDir, '__upload.zip');
run($appDir, cmd('unzip %s', '__upload.zip'));
run($appDir, cmd('rm %s', '__upload.zip'));
run($appDir, cmd('wget %s', 'https://getcomposer.org/composer.phar'));
run($appDir, cmd('php %s/composer.phar install', $appDir));
$port = 8080;
do {
$port++;
$process = run(null, cmd('nc -z 127.0.0.1 %s', $port));
} while ($process->isSuccessful());
background($appDir, cmd('php -S %s app.php', '127.0.0.1:'.$port));
return $app->json([
'id' => $appId,
'port' => $port,
]);
});
$app->run();
function randomBytes($n) {
$fd = fopen('/dev/urandom', 'r');
$bytes = fread($fd, $n);
fclose($fd);
return $bytes;
}
function run($cwd, $cmd) {
$process = new Process($cmd, $cwd);
$process->run();
return $process;
}
function background($cwd, $cmd) {
$client = stream_socket_client('unix://supervisor.ipc', $errno, $errstr);
if (!$client) {
throw new Exception($errstr, $errno);
}
$data = [
'cmd' => $cmd,
'cwd' => $cwd,
];
fwrite($client, json_encode($data));
}
function cmd($cmd, ...$args) {
return vsprintf($cmd, array_map('escapeshellarg', $args));
}
{
"require": {
"silex/silex": "^1.0",
"symfony/process": "^3.0"
}
}
<?php
use Symfony\Component\Process\Process;
require 'vendor/autoload.php';
$server = stream_socket_server('unix://supervisor.ipc', $errno, $errstr);
if (!$server) {
throw new Exception($errstr, $errno);
}
while ($conn = stream_socket_accept($server, -1)) {
$request = stream_get_line($conn, 1024);
$data = json_decode($request, true);
echo $data['cmd']."\n";
$process = new Process($data['cmd'], $data['cwd']);
$process->start();
fclose($conn);
}
fclose($server);
@afolson
Copy link

afolson commented Dec 21, 2015

I accept your blame.

@claudiug
Copy link

+1

@zixan
Copy link

zixan commented Dec 21, 2015

👍

@buley
Copy link

buley commented Dec 21, 2015

I don't think you get to claim 100 lines of code when you use Symfony but 👍 regardless. PHP in 2015 has changed a lot from PHP in 2010, hasn't it?

@mdgrech
Copy link

mdgrech commented Dec 21, 2015

Interesting.

@penoonan
Copy link

🎱

@waldirbertazzijr
Copy link

cool

@davidjeddy
Copy link

I now blame Amanda for this.

@hoohack
Copy link

hoohack commented Dec 22, 2015

looks great

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment