It took about 2 working weeks to finish the app. I made 4 iterations of the app, and each duration mentioned on each step reflects this. Most of my time went into styling the application since I opted to use raw CSS. The hosted solution can be found here: https://expensify.fly.dev/

The project was quite simple to start since the project development steps were pretty much predetermined.

Step 1: Hosting basic PHP environment

Duration: Approximately 2 hours

Since I’m a MacOS user, installing PHP was quite straightforward:

# don't skip this otherwise, you'll be stuck for hours trying to figure out what the bash errors mean. 😢 
brew update
brew install

I used the PHP Server VScode extension to serve my app and PHP DEVSENSE VScode extension for PHP development tools like code completion.

Step 2: Understanding Tech Stack constraints and building PHP proxy file.

Duration: Approximately 4 days

I liked that my choices were narrowed down for me. Otherwise, I would’ve been paralyzed by the tons of choices not only in the JavaScript ecosystem but also in PHP.

This is the tech stack I used for the app:

Building the PHP proxy file took longer than I’d liked:

// proxy.php

<?php

// 1.
$url = $_REQUEST["url"];

if (!$url) {
    echo "You need to pass in a target URL.";
    return;
}

// 2.
$response = "";
switch (getMethod()) {
    case 'POST':
        $response = makePostRequest(getPostData(), $url);
        break;
    case 'GET':
        $response = makeGetRequest(getGetData($url), $url);
        break;
    default:
        echo "This proxy only supports POST AND GET REQUESTS.";
        return;
}

echo $response;

function getMethod()
{
    return $_SERVER["REQUEST_METHOD"];
}

// 3.
function makePostRequest($data, $url)
{
    $httpHeader = array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($data)
    );

    return makePostCurl('POST', $data, true, $httpHeader, $url);
}

// 4.
function makeGetRequest($data, $url)
{
    $ch = initCurl($url);
    curl_setopt($ch, CURLOPT_URL, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    curl_close($ch);

    return $response;
}

// 5.
function getPostData()
{
    return http_build_query($_POST);
}

// 6.
function getGetData($url)
{
    $daurl = http_build_query($_GET);
    $daurl = urldecode($daurl);

    return substr($daurl, 4);
}

// 7.
function initCurl($url)
{

    $httpHeader = array(
        'Content-Type: application/x-www-form-urlencoded'
    );

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeader);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36');

    return $ch;
}

// 8.
function makePostCurl($type, $data, $returnTransfer, $httpHeader, $url)
{
    $ch = initCurl($url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, $returnTransfer);

    $response = curl_exec($ch);
    curl_close($ch);
    return $response;
}

?>
  1. Retrieves the target URL from the query string parameter, url.