As I said in a separate post, I am a Laravel developer amongst other things and recently, I bought a 12” iPad Pro from Apple. I do a lot of writing and I also consume a lot of media on YouTube and Netflix. I also enjoy sketching and prototyping every once in a while. So the iPad Pro was a no-brainer. I was scared of the limitations of iOS though but bought it anyway. Days later, iPadOS was announced — never been happier about a purchase.

However, as a developer at heart, there was one thing I wanted to be able to do the most on my iPad: programming. I checked out many tools but most of them required me to SSH to some server and edit from there. No. I wanted something that could run offline and be able to preview without the need for a stable internet connection. Enter DraftCode.
With DraftCode, I had the ability to run PHP applications offline. Although the editor itself is less than impressive, with no support for auto complete and basic editor features, DraftCode’s ability to run PHP offline was what I needed. Paired with my other development tools, it seemed like I could finally play around with projects without having to whip out my MacBook.

Some other tools I use are:
- Working Copy — a good git client for the iPad
- Prompt — My preferred SSH client
- TablePlus — Database management
- Dash — Offline documentation browser
The problem
The problem with DraftCode became apparent when I wanted to start building apps. You see DraftCode does not have a CLI and cannot run composer commands. This means you can only run the installation on a PC, commit the vendor
directory, and then import everything into DraftCode. This was a pain. I did not want to have to pick up my laptop every time I wanted to start a new project.
This problem was why I built Cmpsr. This is not a full product by any means and it is not to be treated as such, at least not yet.

How does it work
Cmpsr works by allowing you provide a composer.json
file and it then returns the bundled vendor
directory. Using the bundled helper makes it as easy as just running a PHP script and it will generate the ZIP and unzip to the vendor
directory. If you want to add new packages, just update the cmpsr.json
or composer.json
file and resend your request.
Running PHP & Laravel on Your iPad
You should be able to complete all these steps from your iPad Pro running iPadOS. Download the latest version of Laravel from the repository using Safari.

Next open DraftCode and import the Laravel framework into the editor. This will be a ZIP so just unzip the project using the unzip tool that comes with DraftCode. When you have done this, you should now have a familiar Laravel installation on your DraftCode.
Next, go to the cmpsr repository and download the cmpsr.php
file (or just create a new file in DraftCode and paste the contents into it). The file should look something like this:
<?php
/**
* Cmpsr
*
* This package was writted mostly because developing on an iPad is very limited
* and DraftCode, although has offline running capability, did not have support for
* Composer. This package will thus use the cmpsr.co API to generate the composer
* packages outside and then return the ZIP as a package. It is by no means perfect
* and I will be updating it as time goes.
*
* @author Neo Ighodaro <neo@creativitykills.co>
* @package Cmpsr
* @version 1.0
* @license MIT
*/
defined('CMPSR_BASE_URL') or define('CMPSR_BASE_URL', 'https://cmpsr.co');
/**
* @return void
*/
function cmpsr_install(): void
{
$composerFile = file_exists(__DIR__ . '/cmpsr.json')
? __DIR__ . '/cmpsr.json'
: __DIR__ . '/composer.json';
if (!file_exists($composerFile)) {
throw new Exception('Could not locate a "cmpsr.json" or "composer.json" file.');
}
$contents = file_get_contents($composerFile);
$data = ['data' => _cmpsr_json_recode($contents)];
$response = json_decode(_cmpsr_send_request($data), true);
if (!$response['status'] ?? false) {
throw new Exception('An error occurred. Err: ' . $response['error'] ?? 'unknown');
}
$filename = basename($response['url']);
echo "Downloading ZIP file from {$response['url']}..." . PHP_EOL;
_cmpsr_download_file($response['url'], $filename);
if (!file_exists($filename)) {
echo "Error downloading ZIP file..." . PHP_EOL;
}
echo "Download complete. Unzipping..." . PHP_EOL;
$zip = new ZipArchive;
if ($zip->open($filename)) {
$res = $zip->extractTo(__DIR__);
$zip->close();
}
echo ((isset($res) && $res) ? 'Unzipped successfully!' : 'Failed to unzip') . PHP_EOL;
unlink($filename);
echo "Complete.";
}
/**
* @return void
*/
function cmpsr_fetch(string $hash): void
{
// Try to fetch
// If fails then try to install
}
/**
* @return string
*/
function _cmpsr_json_recode(string $contents): string
{
return json_encode(json_decode($contents));
}
/**
* @param array $data
* @return string|null
*/
function _cmpsr_send_request(array $data): string
{
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => CMPSR_BASE_URL . "/install",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 300,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => array(
"Accept: application/json",
"Content-Type: application/json",
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
return empty($err) ? $response : json_encode(['status' => false, 'error' => $err]);
}
/**
* @param string $url
* @param string $path
* @return void
*/
function _cmpsr_download_file($url, $path): void
{
$newfilename = $path;
if ($file = fopen($url, "rb")) {
$newfile = fopen($newfilename, "wb");
if ($newfile) {
while (!feof($file)) {
fwrite($newfile, fread($file, 1024 * 8), 1024 * 8);
}
}
}
if ($file) {
fclose($file);
}
if ($newfile ?? false) {
fclose($newfile);
}
}
cmpsr_install();
This is just a sample way to fetch the packages but you can and should build one yourself.
Next, it is recommended to duplicate your composer.json
file to cmpsr.json
so that cmpsr can use this file. The gotcha is that you will need to update both files whenever you want to update the requirements. Finally, you need to make sure the json file does not contain any of the keys in the known limitations section of the readme.
Now run the cmpsr.php
file inside DraftCode. It will take a while to generate the vendor directories and you will see absolutely no output till it’s completed so have some patience. When it’s done, you should now have the vendor
directory as expected.

Making some other things work
If you are just building a regular PHP package or application, this might be good enough and you can just run the PHP file you want to run, however, if you are building a Laravel application you need to update something to make it (somewhat) work with routes.
Open the index.php
file in the public
directory and update the code as seen below:
if ($_GET['route'] ?? false) {
$_SERVER['REQUEST_URI'] = $_GET['route'];
}
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
Great now you can launch the index file and when you want to visit a route, you have to append the route
query parameter. A minor annoyance but it’s tolerable.
Oh, you also need to create the usual .env
file and add the application key to the file. Now you’re good to go.

From here I think you got it. You can also add Working Copy for some git integration. If you play your cards right, you can probably have the code on git and then build on your MacBook when you are on there and also build on your iPad when you don’t have your MacBook with you. Wins!
Gotcha!
For now, Cmpsr is free and open-source but this may change in the future, if you wish to contribute, let me know. Also it is hosted on a relatively low-powered server so it might make more sense to host it on your own server.
If you have tried it and it works for you let me know. If you run into problems also let me know. There may be more limitations that I may have missed. If you have other tips, you can also share them in the comments section below.