Showing posts with label cURL. Show all posts
Showing posts with label cURL. Show all posts

Sunday, 29 March 2026

Web Tutorial: Chuck Norris Memorial

A legend has left us. On the 19th of this month, one Chuck Norris, martial artist and action movie icon, passed away. One of the things that really stood out in the Chuck Norris mythos was... well, the Chuck Norris mythos. Remember back in the 2000s, how popular "Chuck Norris facts" became?

Well, today, in loving ass-kicking memory, we'll do something like this! It'll be a page that returns a random Chuck Norris fact each time. But this is a tech blog, so the fact has to be tech-based! And this is an image that we'll be using, which I generated using Meta AI.

chucknorris.jpg

Let's begin by creating a PHP page. We'll deal with the HTML portion first. We'll also use some jQuery UI to create nice animations. Note that in the body, we have div tags styled using the CSS classes number, fact and rip. 
<!DOCTYPE html>
<html>
  <head>
    <title>In Memory of Chuck Norris</title>

    <style>
  
    </style>

    <script src="https://code.jquery.com/jquery-3.7.1.js"></script>
    <script src="https://code.jquery.com/ui/1.13.3/jquery-ui.js"></script>

    <script>

    </script>
  </head>

  <body>
    <div class="number"></div>
    <br />
    <div class="fact"></div>
    <div class="rip">R.I.P 19<sup>th</sup> March 2026</div>
  </body>
</html>


Let's add some PHP. This currently is just one line, declaring fact as a string. The value is one of my favorite Chuck Norris "facts". In the div styled using the CSS class fact, display the value of fact. And in the div styled using the CSS class number, let's have a random number to humorously display which number this "fact" is supposed to be.
<?php
  $fact = "Chuck Norris can divide by zero";
?>


<!DOCTYPE html>
<html>
  <head>
    <title>In Memory of Chuck Norris</title>

    <style>
  
    </style>

    <script src="https://code.jquery.com/jquery-3.7.1.js"></script>
    <script src="https://code.jquery.com/ui/1.13.3/jquery-ui.js"></script>

    <script>

    </script>
  </head>

  <body>
    <div class="number">Fact #<?php echo rand(100, 100000); ?>:</div>
    <br />
    <div class="fact"><?php echo $fact; ?></div>
    <div class="rip">R.I.P 19<sup>th</sup> March 2026</div>
  </body>
</html>


This is just text right now.


Now let's style the body tag. We first want to use the image as a background.
<style>
  body
  {
    background: url(chucknorris.jpg) left top no-repeat;
    background-size: cover;
  }  

</style>


Then we want to style the text. I made it large font, with a white outline using the text-shadow property.
<style>
  body
  {
    background: url(chucknorris.jpg) left top no-repeat;
    background-size: cover;
    font-size: 60px;
    font-family: georgia;
    text-shadow: -2px -2px 2px rgb(255, 255, 255), 2px -2px 2px rgb(255, 255, 255), -2px 2px 2px rgb(255, 255, 255), 2px 2px 2px rgb(255, 255, 255);

  }  
</style>


Nice contrast, eh?


Now let's focus on the CSS classes number, fact and rip. It's mostly positional. I made number bolder and floated it left. fact is also floated left. rip has position property set to fixed and is anchored to the bottom right of the screen via the right and bottom properties. I'ave also adjusted the font size.
<style>
  body
  {
    background: url(chucknorris.jpg) left top no-repeat;
    background-size: cover;
    font-size: 60px;
    font-family: georgia;
    text-shadow: -2px -2px 2px rgb(255, 255, 255), 2px -2px 2px rgb(255, 255, 255), -2px 2px 2px rgb(255, 255, 255), 2px 2px 2px rgb(255, 255, 255);
  }

  .number
  {
    font-weight: bold;
    width: 10em;
    float: left;
  }

  .fact
  {
    width: 20em;
    float: left;
  }

  .rip
  {
    font-size: 0.5em;
    height: 1.5em;
    position: fixed;
    bottom: 0;
    right: 0;
  }    
  
</style>


See what I mean?


OK, next up... animation! For this, we want to set the display property of the fact CSS class, to none. This effectively hides the "fact". That's because we want to use jQuery to make it fade in.
.fact
{
  width: 20em;
  float: left;
  display: none;
}


In the script tag, do this so that the code only runs once the HTML is loaded.
<script>
  $(document).ready(function() {

  });

</script>


This causes the "fact" to fade in over the course of 5 seconds.
<script>
  $(document).ready(function() {
    $(".fact").fadeIn(5000);
  });
</script>


Then we use the effect() method on this element. The "bounce" effect belongs to jQuery UI, and here we specify a 1 second duration.
<script>
  $(document).ready(function() {
    $(".number").effect("bounce", 1000);
    $(".fact").fadeIn(5000);
  });
</script>


We include an object with the times property set to 5, so that it bounces 5 times in 1 second. (Sounds like a really lousy credit card, but there you go.)
<script>
  $(document).ready(function() {
    $(".number").effect("bounce", {times: 5}, 1000);
    $(".fact").fadeIn(5000);
  });
</script>


See how the "fact" fades in as the "number" bounces!


Now for the most exciting part... leveraging on OpenAI's ChatGPT to generate a random Chuck Norris "fact". For this, we're leveraging on ChatGPT's API. First, declare key, org and url. These should already have been set up as a new project in ChatGPT. Then create headers as an array of strings. This is what we'll be sending to the URL defined at url.
<?php
  $key = "sk-xxx";
  $org = "org-FUOhDblZb1pxvaY6YylF54gl";
  $url = "https://api.openai.com/v1/chat/completions";

  $headers = [
   "Authorization: Bearer " . $key,
   "OpenAI-Organization: " . $org,
   "Content-Type: application/json"
  ];


  $fact = "Chuck Norris can divide by zero";
?>


We then construct the prompt to send. Here. I specify the JSON object that ChatGPT should give me, and explicitly specify the value. I want a Chuck Norris "fact", and I also want it to be tech-related. Because those are the ones I love. That's for content. role is set to "user". All this is in the array, obj, which is in turn part of messages.
<?php
  $key = "sk-xxx";
  $org = "org-FUOhDblZb1pxvaY6YylF54gl";
  $url = "https://api.openai.com/v1/chat/completions";

  $headers = [
   "Authorization: Bearer " . $key,
   "OpenAI-Organization: " . $org,
   "Content-Type: application/json"
  ];

  $messages = [];
  $obj = [];
  $obj["role"] = "user";
  $obj["content"] = "Give me a JSON object with one property. The property should be named 'fact'. Its value should be a string. This should be a Chuck Norris 'fact', relating either to internet, email or software. An Example would be 'Chuck Norris can divide by zero.'.";
  $messages[] = $obj;


  $fact = "Chuck Norris can divide by zero";
?>


Then we create the parent, data. Here we specify the model. Then we set messages, and max_tokens. This one won't be text-heavy. I reckon 500 tokens should be enough.
<?php
  $key = "sk-xxx";
  $org = "org-FUOhDblZb1pxvaY6YylF54gl";
  $url = "https://api.openai.com/v1/chat/completions";

  $headers = [
   "Authorization: Bearer " . $key,
   "OpenAI-Organization: " . $org,
   "Content-Type: application/json"
  ];

  $messages = [];
  $obj = [];
  $obj["role"] = "user";
  $obj["content"] = "Give me a JSON object with one property. The property should be named 'fact'. Its value should be a string. This should be a Chuck Norris 'fact', relating either to internet, email or software. An Example would be 'Chuck Norris can divide by zero.'.";
  $messages[] = $obj;

  $data = [];
  $data["model"] = "gpt-3.5-turbo";
  $data["messages"] = $messages;
  $data["max_tokens"] = 500;


  $fact = "Chuck Norris can divide by zero";
?>


And here's the final use of cURL, to send data to the API endpoint.
<?php
  $key = "sk-xxx";
  $org = "org-FUOhDblZb1pxvaY6YylF54gl";
  $url = "https://api.openai.com/v1/chat/completions";

  $headers = [
   "Authorization: Bearer " . $key,
   "OpenAI-Organization: " . $org,
   "Content-Type: application/json"
  ];

  $messages = [];
  $obj = [];
  $obj["role"] = "user";
  $obj["content"] = "Give me a JSON object with one property. The property should be named 'fact'. Its value should be a string. This should be a Chuck Norris 'fact', relating either to internet, email or software. An Example would be 'Chuck Norris can divide by zero.'.";
  $messages[] = $obj;

  $data = [];
  $data["model"] = "gpt-3.5-turbo";
  $data["messages"] = $messages;
  $data["max_tokens"] = 500;

  $curl = curl_init($url);
  curl_setopt($curl, CURLOPT_POST, 1);
  curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
  curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

  $result = curl_exec($curl);
  if (curl_errno($curl))
  {
   echo 'Error:' . curl_error($curl);
  }

  curl_close($curl);  


  $fact = "Chuck Norris can divide by zero";
?>


We grab the response and extract the required value. And then we change fact's value from a hard-coded string, to that extracted value.
<?php
  $key = "sk-xxx";
  $org = "org-FUOhDblZb1pxvaY6YylF54gl";
  $url = "https://api.openai.com/v1/chat/completions";

  $headers = [
   "Authorization: Bearer " . $key,
   "OpenAI-Organization: " . $org,
   "Content-Type: application/json"
  ];

  $messages = [];
  $obj = [];
  $obj["role"] = "user";
  $obj["content"] = "Give me a JSON object with one property. The property should be named 'fact'. Its value should be a string. This should be a Chuck Norris 'fact', relating either to internet, email or software. An Example would be 'Chuck Norris can divide by zero.'.";
  $messages[] = $obj;

  $data = [];
  $data["model"] = "gpt-3.5-turbo";
  $data["messages"] = $messages;
  $data["max_tokens"] = 500;

  $curl = curl_init($url);
  curl_setopt($curl, CURLOPT_POST, 1);
  curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
  curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

  $result = curl_exec($curl);
  if (curl_errno($curl))
  {
   echo 'Error:' . curl_error($curl);
  }

  curl_close($curl);  

  $data = [];
  $data["model"] = "gpt-3.5-turbo";
  $data["messages"] = $messages;
  $data["max_tokens"] = 500;

  $result = json_decode($result);
  $content = $result->choices[0]->message->content;
  $content = json_decode($content);


  $fact = $content->fact;  
?>


See? The facts change now.


Different fact.


Another different fact.


R.I.P, Mr Norris!

Rumour has it that you've been dead for years. Death just hasn't plucked up the courage to tell you.

Did you know that Chuck Norris can delete the Recycle Bin?
T___T

Monday, 24 March 2025

Web Tutorial: The Movie Poster Generator

Hi, readers. Do I have a fun one today!

We will be leveraging on the code I wrote for the Nike Meme Generator, to generate something else that may require an image upload - a Movie Poster Generator! This one is going to require some ChatGPT finangling as well.

Let's begin by setting up the uploads directory, and adding a default image - an AI-generated portrait of Angelina Jolie.
uploads/angelinajolie.jpg

Then, in the parent folder, we copy over the code from the Nike Meme Generator in index.php. We'll be removing quite a lot of lines. You'll see what we've retained is the file upload functions and the form. We've also made a few text changes as well. The div memeContainer has been replaced by posterContainer, along with styling.
<?php
$filecode = "angelinajolie";
$filetype = "jpg";
//$line1 = "Believe in something.";
//$line2 = "Even if it means sacrificing everything.";
//$slogan = "Just Do It.";

$strmessage="";

if (isset($_POST["btSubmit"]))
{
    //$line1 = $_POST["txtLine1"];
    //$line2 = $_POST["txtLine2"];
    //$slogan = $_POST["txtSlogan"];

    if (basename($_FILES["flUpload"]["name"]) != "")
    {
        $uploadsize = intval($_POST["hidUploadSize"]);
         $filetype = pathinfo($_FILES["flUpload"]["name"],PATHINFO_EXTENSION);
         $filetype = strtolower($filetype);

         if ($_FILES["flUpload"]["size"] > $uploadsize)
         {
             $strmessage = "Error was encountered while uploading file. File cannot exceed " . ($uploadsize/1000) . "kb";
        }
        else
        {
          if (!is_array(getimagesize($_FILES["flUpload"]["tmp_name"])))
          {
           $strmessage = "File type invalid";
          }
          else
          {
              $filecode=strtotime("now").rand();
        
              if (move_uploaded_file($_FILES["flUpload"]["tmp_name"], "uploads/" . $filecode . "." . $filetype))
              {
               $strmessage = "File uploaded.";
              }
              else
              {
                  $strmessage = "Error was encountered while uploading file.";
              }  
          }
         }
    }
    else
    {
         $strmessage="No file selected.";
    }
}
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Movie Poster Generator</title>

        <style>
        #pnlMessage
        {
            width: 100%;
            height: 50px;
            color: #FF0000;
            outline: 0px solid #DDDDDD;
        }

        #formContainer
        {
            width: 400px;
            padding: 5px;
            margin: 5px;
            float: left;
            outline: 0px solid #DDDDDD;
        }

        /*
        #memeContainer
        {
            width: 500px;
            height: 500px;
            padding: 5px;
            margin: 5px;
            float: left;
            outline: 1px solid #DDDDDD;
            background: url(<?php echo "uploads/" . $filecode . "." . $filetype; ?>) center center no-repeat;
            background-size: cover;
            font-family: georgia;
            color: #FFFFFF;
            font-size: 25px;
            -webkit-filter: grayscale(100%);
            filter: grayscale(100%);
            text-align: center;
        }
         */

        #posterContainer
        {
            width: 800px;

            height: 500px;
            margin: 5px;
            float: left;
            text-align: center;
        }

        @media print
        {
               #formContainer, #pnlMessage
               {
                   display: none;
               }
    
               #posterContainer
               {
                    margin: 10% auto 0 auto;
                    float: none;
               }
        }
        </style>
    </head>

    <body>
        <div id="pnlMessage"><?php echo $strmessage; ?></div>

        <div id="formContainer">
            <form id="frmUpload" name="frmUpload" action="" method="POST" enctype="multipart/form-data">
                  <label for="flUpload">File</label>
                  <input type="file" name="flUpload" id="flUpload">
                  <input type="hidden" name="hidUploadSize" id="hidUploadSize" value="50000000">
                  <br /><br />
                  <!---
                 <label for="txtLine1">Line 1</label>
                  <input name="txtLine1" id="txtLine1" maxlength="50" value="<?php echo $line1; ?>" />
                  <br /><br />
                 <label for="txtLine2">Line 2</label>
                  <input name="txtLine2" id="txtLine2" maxlength="50" value="<?php echo $line2; ?>" />
                  <br /><br />
                  <label for="txtSlogan">Slogan</label>
                  <input name="txtSlogan" id="txtSlogan" maxlength="20" value="<?php echo $slogan; ?>" />
                  --->
                  <br /><br />
                  <input type="submit" name="btSubmit" id="btSubmit" value="Create your Movie Poster!">
            </form>
        </div>

        <div id="posterContainer">
             <!---
             <p style="margin-top:50%"><?php echo $line1;?><br /><?php echo $line2;?></p>
             <p style="margin-top:30%"><img src="nikelogo.png"> <?php echo $slogan;?></p>
             --->
        </div>
    </body>
</html>


Because I plan on using jQuery in here, let's include the library as well. And a script tag for custom JavaScript.
<head>
  <title>Movie Poster Generator</title>

  <style>
    #pnlMessage
    {
      width: 100%;
      height: 50px;
      color: #FF0000;
      outline: 0px solid #DDDDDD;
    }

    #formContainer
    {
      width: 400px;
      padding: 5px;
      margin: 5px;
      float: left;
      outline: 0px solid #DDDDDD;
    }

    #posterContainer
    {
      width: 800px;
      height: 500px;
      margin: 5px;
      float: left;
      text-align: center;
    }

    @media print
    {
        #formContainer, #pnlMessage
        {
          display: none;
        }

        #posterContainer
        {
          margin: 10% auto 0 auto;
          float: none;
        }
    }
  </style>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script>

  </script>

</head>


We are going to begin the PHP with some variables other than strmessage, filecode and filetype. You'll see that I've specified some default values too.

space_from_top: controls the vertical position that the block of text occupies.
movie_title: self-explanatory.
movie_title_color: what color it appears in.
movie_title_size: what font size to use for the title.
movie_tagline: the tagline that appears beneath the title.
movie_tagline_color: what color it appears in.
movie_tagline_size: what font size to use for the tagline.
movie_starring: the movie star name(s). Since I'm using a picture of Angeline Jolie, that's her name I'm using.
movie_starring_color: what color it appears in.
movie_starring_size: what font size to use.
reviews: this is an array which will contain the stuff we get from ChatGPT.
reviews_color: what color they appear in.
reviews_bgcolor: what background color the reviews will use.

<?php
$filecode = "angelinajolie";
$filetype = "jpg";

$space_from_top = "20";

$movie_title = "Modern-day Maleficent";
$movie_title_color = "#FFFFFF";
$movie_title_size = "30";

$movie_tagline = "A re-imagining of the original tale of darkness";
$movie_tagline_color = "#FFFFFF";
$movie_tagline_size = "15";

$movie_starring = "Angelina Jolie";
$movie_starring_color = "#FFFFFF";
$movie_starring_size = "10";

$reviews = [];
$reviews_color = "#FFFFFF";
$reviews_bgcolor = "#000000";


$strmessage="";


Inside the posterContainer div, add three divs, left, middle and right. left and right are styled using the CSS class review.
<div id="posterContainer">
    <div id="left" class="review">

    </div>
    <div id="middle">

    </div>
    <div id="right" class="review">

    </div>

</div>


In the styles, both left and right have different text alignments. As they are both styled by the CSS class review, they have a certain width and height, they're floated left with a little padding, and color and background-color properties are determined by reviews_color and reviews_bgcolor respectively. The font type is less important and it's a personal choice.
#posterContainer
{
    width: 800px;
    height: 500px;
    margin: 5px;
    float: left;
     text-align: center;
}

#left
{
    text-align: right;
}

#right
{
    text-align: left;
}

.review
{
    width: 180px;
    height: 480px;
    float: left;
    padding: 10px;
    color: <?php echo $reviews_color;?>;
    background-color: <?php echo $reviews_bgcolor;?>;
    font-family: Georgia;
}

@media print
{
    #formContainer, #pnlMessage
    {
        display: none;
    }

    #posterContainer
    {
        margin: 10% auto 0 auto;
        float: none;
    }
}


Now we have middle. Like the reviews CSS class, it has a certain width and height, and is floated left. The background is determined by filecode, which currently points to angelinajolie.jpg in the uploads folder.
#posterContainer
{
    width: 800px;
    height: 500px;
    margin: 5px;
    float: left;
     text-align: center;
}

#middle
{
    width: 380px;
    height: 500px;
    background: url(<?php echo "uploads/" . $filecode . "." . $filetype; ?>) center center no-repeat;
    background-size: cover;
    float: left;
}

#left
{
    text-align: right;
}

#right
{
    text-align: left;
}


Here's a preview!

In the middle div, add a p tag with the id title_and_tagline. In there, we have span tags with the ids movie_title and movie_tagline respectively.
<div id="middle">
    <p id="title_and_tagline">
    
    <span id="movie_title"></span>
        <br />
        <span id="movie_tagline"></span>
    </p>
</div>


Populate these span tags with the strings for movie_title and movie_tagline.
<div id="middle">
    <p id="title_and_tagline">
        <span id="movie_title"><?php echo $movie_title;?></span>
        <br />
        <span id="movie_tagline"><?php echo $movie_tagline;?></span>
    </p>
</div>


Now add another paragraph with id movie_starring.
<div id="middle">
    <p id="title_and_tagline">
        <span id="movie_title"><?php echo $movie_title;?></span>
        <br />
        <span id="movie_tagline"><?php echo $movie_tagline;?></span>
    </p>
    <p id="movie_starring">

    </p>
</div>


In there, have some PHP. It's possible that movie_starring is an empty string, in which case we want no text in that paragraph. But if it's not an empty string, we want it to say "starring" followed by movie_starring.
<div id="middle">
    <p id="title_and_tagline">
        <span id="movie_title"><?php echo $movie_title;?></span>
        <br />
        <span id="movie_tagline"><?php echo $movie_tagline;?></span>
    </p>
    <p id="movie_starring">
        <?php echo ($movie_starring == "" ? "" : "starring ");?>
        <?php echo $movie_starring;?>
    </p>
</div>


Let's do some more styling. We have styles for title_and_tagline, movie_title, movie_tagline and movie_starring. We're doing largely what we did for the review CSS class, with font-size and color property values determined by the PHP variables. For title_and_tagline, the margin-top property is determined by space_from_top.
.review
{
    width: 180px;
    height: 480px;
    float: left;
    padding: 10px;
    color: <?php echo $reviews_color;?>;
    background-color: <?php echo $reviews_bgcolor;?>;
    font-family: Georgia;
}

#title_and_tagline
{
    margin-top: <?php echo $space_from_top;?>px;
}

#movie_title
{
    color: <?php echo $movie_title_color;?>;
    font-size: <?php echo $movie_title_size;?>px;
    font-weight: bold;
    font-family: Impact, Verdana;
}

#movie_tagline
{
    color: <?php echo $movie_tagline_color;?>;
    font-size: <?php echo $movie_tagline_size;?>px;
    font-family: Arial, Helvetica, Verdana;
}

#movie_starring
{
    color: <?php echo $movie_starring_color;?>;
    font-size: <?php echo $movie_starring_size;?>px;
    font-family: Arial, Helvetica, Verdana;
}

@media print
{
    #formContainer, #pnlMessage
    {
        display: none;
    }

    #posterContainer
    {
        margin: 10% auto 0 auto;
        float: none;
    }
}


You see the text appears!

Now we are going to make randomly-generated reviews appear. Remember the empty array reviews? Basically, we're about to populate it. The code will run only if the form has been submitted, so put it inside the If block. You'll need your own OpenAI Developer Account, so replace "xxx" with your credentials. Then define headers with the appropriate properties for authentication.
if (isset($_POST["btSubmit"]))
{
    if (basename($_FILES["flUpload"]["name"]) != "")
    {
         $uploadsize = intval($_POST["hidUploadSize"]);
         $filetype = pathinfo($_FILES["flUpload"]["name"],PATHINFO_EXTENSION);
         $filetype = strtolower($filetype);

         if ($_FILES["flUpload"]["size"] > $uploadsize)
         {
             $strmessage = "Error was encountered while uploading file. File cannot exceed " . ($uploadsize/1000) . "kb";
         }
         else
         {
             if (!is_array(getimagesize($_FILES["flUpload"]["tmp_name"])))
             {
                  $strmessage = "File type invalid";
             }
             else
             {
                  $filecode=strtotime("now").rand();
        
                  if (move_uploaded_file($_FILES["flUpload"]["tmp_name"], "uploads/" . $filecode . "." . $filetype))
                  {
                       $strmessage = "File uploaded.";
                  }
                  else
                  {
                       $strmessage = "Error was encountered while uploading file.";
                  }  
              }
         }
    }
    else
    {
         $strmessage="No file selected.";
    }

    $key = "xxx";
    $org = "org-xxx";
    $url = 'https://api.openai.com/v1/chat/completions';  

    $headers = [
        "Authorization: Bearer " . $key,
        "OpenAI-Organization: " . $org,
        "Content-Type: application/json"
    ];
}


Here, we're defining the prompt. We use movie_title (and movie_starring, if it's not an empty string), and generate an array, FakeReviews, of 10 objects. Each object will have a one-sentence comment and a string to determine the "source" of the comment.
$key = "xxx";
$org = "org-xxx";
$url = 'https://api.openai.com/v1/chat/completions';  

$headers = [
    "Authorization: Bearer " . $key,
    "OpenAI-Organization: " . $org,
    "Content-Type: application/json"
];

$messages = [];
$obj = [];
$obj["role"] = "user";
$obj["content"] = "Give me a JSON object with one property. The property should be named 'FakeReviews', and should be an array of ten objects. Each object should have the property 'review', which is a random fictional complimentary about the movie '" . $movie_title . "'" . ($movie_starring == "" ? "" : " or celebrity '" . $movie_starring . "'") .  " (range between three to ten words) sentence in a string, and the property 'critic' which contains the fictional publication for that quote.";
$messages[] = $obj;


The rest of the code we've gone through before in The Random Christmas Card and The Self-affirmations Wordpress Plugin. 
$messages = [];
$obj = [];
$obj["role"] = "user";
$obj["content"] = "Give me a JSON object with one property. The property should be named 'FakeReviews', and should be an array of ten objects. Each object should have the property 'review', which is a random fictional complimentary about the movie '" . $movie_title . "'" . ($movie_starring == "" ? "" : " or celebrity '" . $movie_starring . "'") .  " (range between three to ten words) sentence in a string, and the property 'critic' which contains the fictional publication for that quote.";
$messages[] = $obj;

$data = [];
$data["model"] = "gpt-3.5-turbo";
$data["messages"] = $messages;
$data["max_tokens"] = 1000;


We use cURL to send the data to the API endpoint. The returned response is in result, and we handle any errors before closing out with curl_close().
$data = [];
$data["model"] = "gpt-3.5-turbo";
$data["messages"] = $messages;
$data["max_tokens"] = 1000;

$curl = curl_init($url);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

$result = curl_exec($curl);
if (curl_errno($curl)) 

{
    echo 'Error:' . curl_error($curl);
}

curl_close($curl);


Then we'll use json_decode() on result, and then extract FakeReviews from it.
$result = curl_exec($curl);
if (curl_errno($curl)) 
{
    echo 'Error:' . curl_error($curl);
}

curl_close($curl);

$result = json_decode($result);
$content = $result->choices[0]->message->content;
$content = json_decode($content);

$reviews = $content->FakeReviews;


In the left div, use a PHP If block to check if reviews has 10 elements, just to filter out any nasty surprises. Then use a For loop to go through the first 5 elements.
<div id="left" class="review">
<?php
if (count($reviews) == 10) 

{
    for ($i = 0; $i < 5; $i++)
    {

    }
}
?>

</div>


For each element, you want a span element, and a nicely formatted review property. Be sure to use htmlspecialchars() on it.
<div id="left" class="review">
<?php
if (count($reviews) == 10) 
{
    for ($i = 0; $i < 5; $i++)
    {
?>
    <span>
    <b>&ldquo;<?php echo htmlspecialchars($reviews[$i]->review); ?>&rdquo;</b>
    </span>
<?php

    }
}
?>
</div>


We want the font size to be inversely proportional to the length of the string. Thus, if the review was "Go watch it!", it would be in a significantly larger font than "This movie will bring you through a roller-coaster ride of emotions!".
<div id="left" class="review">
<?php
if (count($reviews) == 10) 
{
    for ($i = 0; $i < 5; $i++)
    {
?>
    <span style="font-size:<?php echo (1.5 - (strlen($reviews[$i]->review) / 10)); ?>em">
    <b>&ldquo;<?php echo htmlspecialchars($reviews[$i]->review); ?>&rdquo;</b>
    </span>
<?php
    }
}
?>
</div>


After that, we have a small tag and the critic property in italics.
<div id="left" class="review">
<?php
if (count($reviews) == 10) 
{
    for ($i = 0; $i < 5; $i++)
    {
?>
    <span style="font-size:<?php echo (1.5 - (strlen($reviews[$i]->review) / 10)); ?>em">
    <b>&ldquo;<?php echo htmlspecialchars($reviews[$i]->review); ?>&rdquo;</b>
    </span>
    <br />
    <small>
    <i><?php echo $reviews[$i]->critic; ?></i>
    </small>
    <br />
    <br />

<?php
    }
}
?>
</div>


We then want there to be between 3 to 5 stars. So we use the HTML symbol 3 times...
<small>
<i><?php echo $reviews[$i]->critic; ?></i>
&nbsp;&#9733;&#9733;&#9733;
</small>


Then use a For loop and the rand() function to potentially put down a maximum of 2 more stars.
<small>
<i><?php echo $reviews[$i]->critic; ?></i>
&nbsp;&#9733;&#9733;&#9733;
<?php 
for ($j = 0; $j <= 1; $j++)
{
    if (rand(1, 2) == 1) echo "&#9733;";
}
?>

</small>


You have to click the "Create your Movie Poster" button to test this.

Repeat for the other side. This time, set the For loop to iterate through elements 5 to 10 of reviews.
<div id="right" class="review">
<?php
if (count($reviews) == 10) 

{
    for ($i = 5; $i < 10; $i++)
    {
?>
    <span style="font-size:<?php echo (2 - (strlen($reviews[$i]->review) / 10)); ?>em"><b>&ldquo;<?php echo htmlspecialchars($reviews[$i]->review); ?>&rdquo;</b>
    </span>
    <br />
    <small>
    <i><?php echo $reviews[$i]->critic; ?></i>
    &nbsp;&#9733;&#9733;&#9733;
    <?php 
    for ($j = 0; $j <= 1; $j++)
    {
        if (rand(1, 2) == 1) echo "&#9733;";
    }
    ?>
    </small>
    <br />
    <br />
<?php
    }
}
?>

</div>


Looks like the other side is done!


We're not quite done yet...

We want to make a dashboard to manipulate customizable variables. The good news is, we've done that already before in The Easter Egg Generator, all the way back in 2016. Can't really reuse any code, but the principles are the same.

Let's define some fieldsets, with legends.
<form id="frmUpload" name="frmUpload" action="" method="POST" enctype="multipart/form-data">
    <label for="flUpload">File</label>
    <input type="file" name="flUpload" id="flUpload">
    <input type="hidden" name="hidUploadSize" id="hidUploadSize" value="50000000">
    <br /><br />
    <fieldset>
        <legend>Movie Title</legend>            

    </fieldset>

    <fieldset>
        <legend>Movie Tagline</legend>

    </fieldset>

    <fieldset>
        <legend>Starring</legend>

    </fieldset>           

    <fieldset>
        <legend>Reviews</legend>

    </fieldset>
    <br /><br />

    <input type="submit" name="btSubmit" id="btSubmit" value="Create your Movie Poster!">
</form>


Taking shape!


We'll want controls that the user can use to change the PHP variables. For numeric values, we'll use a range input. For colors, we'll use color inputs. And for text values, we will just have vanilla text inputs. For the latter, I've included maxlength attributes out of sheer habit. Take a note of name and id attributes - those will be relevant real soon.
<input type="hidden" name="hidUploadSize" id="hidUploadSize" value="50000000">
<br /><br />
<label for="txtSpace_from_top">Space From Top</label>
<input type="range" min="10" max="400" name="txtSpace_from_top" id="txtSpace_from_top" value="" />

<br /><br />
<fieldset>
        <legend>Movie Title</legend>
        <label for="txtMovie_title">Text</label>
        <input name="txtMovie_title" id="txtMovie_title" maxlength="20" value="" />
        <br />
        <label for="txtMovie_title_color">Color</label>
        <input type="color" name="txtMovie_title_color" id="txtMovie_title_color" value="" />
        <br />
        <label for="txtMovie_title_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_title_size" id="txtMovie_title_size" value="" />
            
</fieldset>

<fieldset>
        <legend>Movie Tagline</legend>
        <label for="txtMovie_tagline">Text</label>
        <input name="txtMovie_tagline" id="txtMovie_tagline" maxlength="50" value="" />
        <br />
        <label for="txtMovie_tagline_color">Color</label>
        <input type="color" name="txtMovie_tagline_color" id="txtMovie_tagline_color" value="" />
        <br />
        <label for="txtMovie_tagline_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_tagline_size" id="txtMovie_tagline_size" value="" />

</fieldset>

<fieldset>
        <legend>Starring</legend>
        <label for="txtMovie_starring">Text</label>
        <input name="txtMovie_starring" id="txtMovie_starring" maxlength="100" value="" />
        <br />
        <label for="txtMovie_starring_color">Color</label>
        <input type="color" name="txtMovie_starring_color" id="txtMovie_starring_color" value="" />
        <br />
        <label for="txtMovie_starring_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_starring_size" id="txtMovie_starring_size" value="" />

</fieldset>           

<fieldset>
        <legend>Reviews</legend>
        <label for="txtReviews_color">Color</label>
        <input type="color" name="txtReviews_color" id="txtReviews_color" value="" />
        <br />
        <label for="txtReviews_bgcolor">Background Color</label>
        <input type="color" name="txtReviews_bgcolor" id="txtReviews_bgcolor" value="" />

</fieldset>


And here are the inputs. Just a bit messy, so let's clean stuff up.


In the styles, style labels to have a fixed width. I've also styled font. While we're at it, let's have the submit button also given a fixed width, a bit of spacing at the top, and float it right.
#formContainer
{
        width: 400px;
        padding: 5px;
        margin: 5px;
        float: left;
        outline: 0px solid #DDDDDD;
}

label
{
        display: inline-block;
        font-family: arial;
        font-size: 12px;
        width: 10em;
}


#btSubmit
{
        width: 15em;
        margin-top: 1em;
        float: right;
}

#posterContainer
{
        width: 800px;
        height: 500px;
        margin: 5px;
        float: left;
        text-align: center;
}


And then let's populate the values of all these controls with the PHP variables.
<label for="txtSpace_from_top">Space From Top</label>
<input type="range" min="10" max="400" name="txtSpace_from_top" id="txtSpace_from_top" value="<?php echo $space_from_top; ?>" />
<br /><br />
<fieldset>
        <legend>Movie Title</legend>
        <label for="txtMovie_title">Text</label>
        <input name="txtMovie_title" id="txtMovie_title" maxlength="20" value="<?php echo $movie_title; ?>" />
        <br />
        <label for="txtMovie_title_color">Color</label>
        <input type="color" name="txtMovie_title_color" id="txtMovie_title_color" value="<?php echo $movie_title_color; ?>" />
        <br />
        <label for="txtMovie_title_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_title_size" id="txtMovie_title_size" value="<?php echo $movie_title_size; ?>" />             
</fieldset>

<fieldset>
        <legend>Movie Tagline</legend>
        <label for="txtMovie_tagline">Text</label>
        <input name="txtMovie_tagline" id="txtMovie_tagline" maxlength="50" value="<?php echo $movie_tagline; ?>" />
        <br />
        <label for="txtMovie_tagline_color">Color</label>
        <input type="color" name="txtMovie_tagline_color" id="txtMovie_tagline_color" value="<?php echo $movie_tagline_color; ?>" />
        <br />
        <label for="txtMovie_tagline_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_tagline_size" id="txtMovie_tagline_size" value="<?php echo $movie_tagline_size; ?>" />
</fieldset>

<fieldset>
        <legend>Starring</legend>
        <label for="txtMovie_starring">Text</label>
        <input name="txtMovie_starring" id="txtMovie_starring" maxlength="100" value="<?php echo $movie_starring; ?>" />
        <br />
        <label for="txtMovie_starring_color">Color</label>
        <input type="color" name="txtMovie_starring_color" id="txtMovie_starring_color" value="<?php echo $movie_tagline_color; ?>" />
        <br />
        <label for="txtMovie_starring_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_starring_size" id="txtMovie_starring_size" value="<?php echo $movie_starring_size; ?>" />
</fieldset>           

<fieldset>
        <legend>Reviews</legend>
        <label for="txtReviews_color">Color</label>
        <input type="color" name="txtReviews_color" id="txtReviews_color" value="<?php echo $reviews_color; ?>" />
        <br />
        <label for="txtReviews_bgcolor">Background Color</label>
<input type="color" name="txtReviews_bgcolor" id="txtReviews_bgcolor" value="<?php echo $reviews_bgcolor; ?>" />
</fieldset>


There you go.


Now, here's some more PHP code. This ensures that if you change any of the variables in the form and then submit the form, the changes take effect.
if (isset($_POST["btSubmit"]))
{
    if (basename($_FILES["flUpload"]["name"]) != "")
    {
         $uploadsize = intval($_POST["hidUploadSize"]);
         $filetype = pathinfo($_FILES["flUpload"]["name"],PATHINFO_EXTENSION);
         $filetype = strtolower($filetype);

         if ($_FILES["flUpload"]["size"] > $uploadsize)
         {
             $strmessage = "Error was encountered while uploading file. File cannot exceed " . ($uploadsize/1000) . "kb";
         }
         else
         {
             if (!is_array(getimagesize($_FILES["flUpload"]["tmp_name"])))
             {
                  $strmessage = "File type invalid";
             }
             else
             {
                  $filecode=strtotime("now").rand();
        
                  if (move_uploaded_file($_FILES["flUpload"]["tmp_name"], "uploads/" . $filecode . "." . $filetype))
                  {
                      $strmessage = "File uploaded.";
                  }
                  else
                  {
                      $strmessage = "Error was encountered while uploading file.";
                  }  
             }
         }
    }
    else
    {
         $strmessage="No file selected.";
    }

    $space_from_top = $_POST["txtSpace_from_top"];

    $movie_title = $_POST["txtMovie_title"];
    $movie_title_color = $_POST["txtMovie_title_color"];
    $movie_title_size = $_POST["txtMovie_title_size"];

    $movie_tagline = $_POST["txtMovie_tagline"];
    $movie_tagline_color = $_POST["txtMovie_tagline_color"];
    $movie_tagline_size = $_POST["txtMovie_tagline_size"];

    $movie_starring = $_POST["txtMovie_starring"];
    $movie_starring_color = $_POST["txtMovie_starring_color"];
    $movie_starring_size = $_POST["txtMovie_starring_size"];

    $reviews = [];
    $reviews_color = $_POST["txtReviews_color"];
    $reviews_bgcolor = $_POST["txtReviews_bgcolor"];


    $key = "xxx";
    $org = "org-xxx";
    $url = 'https://api.openai.com/v1/chat/completions';  

    $headers = [
        "Authorization: Bearer " . $key,
        "OpenAI-Organization: " . $org,
        "Content-Type: application/json"
    ];

    $messages = [];
    $obj = [];
    $obj["role"] = "user";
    $obj["content"] = "Give me a JSON object with one property. The property should be named 'FakeReviews', and should be an array of ten objects. Each object should have the property 'review', which is a random fictional complimentary about the movie '" . $movie_title . "'" . ($movie_starring == "" ? "" : " or celebrity '" . $movie_starring . "'") .  " (range between three to ten words) sentence in a string, and the property 'critic' which contains the fictional publication for that quote.";
    $messages[] = $obj;

    $data = [];
    $data["model"] = "gpt-3.5-turbo";
    $data["messages"] = $messages;
    $data["max_tokens"] = 1000;

    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($curl);
    if (curl_errno($curl)) 
    {
         echo 'Error:' . curl_error($curl);
    }

    curl_close($curl);

    $result = json_decode($result);
    $content = $result->choices[0]->message->content;
    $content = json_decode($content);

    $reviews = $content->FakeReviews;
}


See what I mean?


Now, it would be nice to have whatever changes you make, be reflected in "real-time" instead of having to submit the form. Well, image changes have to involve submitting the form, but not the rest! So, in the fields, add the oninput attribute and set it to call the function useVariables().
<label for="txtSpace_from_top">Space From Top</label>
<input type="range" min="10" max="400" name="txtSpace_from_top" id="txtSpace_from_top" value="<?php echo $space_from_top; ?>" oninput="useVariables()" />
<br /><br />
<fieldset>
        <legend>Movie Title</legend>
        <label for="txtMovie_title">Text</label>
        <input name="txtMovie_title" id="txtMovie_title" maxlength="20" value="<?php echo $movie_title; ?>" oninput="useVariables()" />
        <br />
        <label for="txtMovie_title_color">Color</label>
        <input type="color" name="txtMovie_title_color" id="txtMovie_title_color" value="<?php echo $movie_title_color; ?>" oninput="useVariables()" />
        <br />
        <label for="txtMovie_title_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_title_size" id="txtMovie_title_size" value="<?php echo $movie_title_size; ?>" oninput="useVariables()" />             
</fieldset>

<fieldset>
        <legend>Movie Tagline</legend>
        <label for="txtMovie_tagline">Text</label>
        <input name="txtMovie_tagline" id="txtMovie_tagline" maxlength="50" value="<?php echo $movie_tagline; ?>" oninput="useVariables()" />
        <br />
        <label for="txtMovie_tagline_color">Color</label>
        <input type="color" name="txtMovie_tagline_color" id="txtMovie_tagline_color" value="<?php echo $movie_tagline_color; ?>" oninput="useVariables()" />
        <br />
        <label for="txtMovie_tagline_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_tagline_size" id="txtMovie_tagline_size" value="<?php echo $movie_tagline_size; ?>" oninput="useVariables()" />
</fieldset>

<fieldset>
        <legend>Starring</legend>
        <label for="txtMovie_starring">Text</label>
        <input name="txtMovie_starring" id="txtMovie_starring" maxlength="100" value="<?php echo $movie_starring; ?>" oninput="useVariables()" />
        <br />
        <label for="txtMovie_starring_color">Color</label>
        <input type="color" name="txtMovie_starring_color" id="txtMovie_starring_color" value="<?php echo $movie_tagline_color; ?>" oninput="useVariables()" />
        <br />
        <label for="txtMovie_starring_size">Size</label>
        <input type="range" min="10" max="50" name="txtMovie_starring_size" id="txtMovie_starring_size" value="<?php echo $movie_starring_size; ?>" oninput="useVariables()" />
</fieldset>           

<fieldset>
        <legend>Reviews</legend>
        <label for="txtReviews_color">Color</label>
        <input type="color" name="txtReviews_color" id="txtReviews_color" value="<?php echo $reviews_color; ?>" oninput="useVariables()" />
        <br />
        <label for="txtReviews_bgcolor">Background Color</label>
<input type="color" name="txtReviews_bgcolor" id="txtReviews_bgcolor" value="<?php echo $reviews_bgcolor; ?>" oninput="useVariables()" />
</fieldset>


And then we create the useVariables() function in the JavaScript.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
function useVariables()
{

}

</script>


We will be making changes to these elements...
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
function useVariables()
{
    $("#title_and_tagline")

    $(".review")

    $("#movie_title")

    $("#movie_tagline")

    $("#movie_starring")

}
</script>


All of these elements will have changes made to the style attribute. As you can see, the changes are mostly about font size and color. In the case of title_and_tagline, the margin-top property is changed. In the case of divs styled using the review CSS class, it's color and background color that's changed.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
function useVariables()
{
    $("#title_and_tagline")
    .attr("style", "margin-top:" + $("#txtSpace_from_top").val() + "px");

    $(".review")
    .attr("style", "color:" + $("#txtReviews_color").val() + ";background-color: " +       $("#txtReviews_bgcolor").val());

    $("#movie_title")
    .attr("style", "color:" + $("#txtMovie_title_color").val() + ";font-size: " + $("#txtMovie_title_size").val() + "px");

    $("#movie_tagline")
    .attr("style", "color:" + $("#txtMovie_tagline_color").val() + ";font-size: " + $("#txtMovie_tagline_size").val() + "px");

    $("#movie_starring")
    .attr("style", "color:" + $("#txtMovie_starring_color").val() + ";font-size: " + $("#txtMovie_starring_size").val() + "px");
}
</script>


For movie_title, movie_tagline and movie_starring, we use the html() method to change the text. Note that for movie_starring, the same rules apply as they did with the PHP script - if the contents of the txtMovie_starring text box is an empty string, this placeholder will be empty as well; otherwise, prepend with "starring".
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
function useVariables()
{
    $("#title_and_tagline")
    .attr("style", "margin-top:" + $("#txtSpace_from_top").val() + "px");

    $(".review")
    .attr("style", "color:" + $("#txtReviews_color").val() + ";background-color: " + $("#txtReviews_bgcolor").val());

    $("#movie_title")
    .attr("style", "color:" + $("#txtMovie_title_color").val() + ";font-size: " + $("#txtMovie_title_size").val() + "px")
    .html($("#txtMovie_title").val());

    $("#movie_tagline")
    .attr("style", "color:" + $("#txtMovie_tagline_color").val() + ";font-size: " + $("#txtMovie_tagline_size").val() + "px")
    .html($("#txtMovie_tagline").val());

    $("#movie_starring")
    .attr("style", "color:" + $("#txtMovie_starring_color").val() + ";font-size: " + $("#txtMovie_starring_size").val() + "px")
    .html(($("#txtMovie_starring").val() == "" ? "" : "starring " + $("#txtMovie_starring").val()));
}
</script>


You can't really tell from here, but the screen is reflecting the changes I'm making right now.


Before I forget...

You can even print out your poster. The media queries I specified in the CSS ensure that the dashboard is not visible in print view.

Now, wasn't that fun?!

Generative Artificial Intelligence really injects a little bit of random craziness into tiny projects like these. Love it!

Getting the picture? Hur hur,
T___T