PHP & Laravel — Zero to Hero Episode 09: Forms and User Input — Making PHP Interactive

What Are We Doing in This Post?

So far every PHP program we wrote had fixed data. We hardcoded names, prices, and scores directly in the code. That is fine for learning — but real websites do not work that way.

Real websites take input from users. A user types their name, clicks a button, and the page responds. That is what forms do. And PHP is what processes them on the server.

This episode is where PHP stops being a calculator and starts being a real web application.


How Do HTML Forms and PHP Work Together?

Here is the flow, step by step.

A user opens a webpage that has an HTML form — text fields, buttons, dropdowns.

The user fills in the form and clicks Submit.

The browser sends that data to the server via an HTTP request.

PHP on the server receives that data, processes it, and sends a response back.

The user sees the result.

This entire cycle happens in under a second on real websites.


GET vs POST — The Two Ways to Send Form Data

When an HTML form sends data to the server, it uses one of two methods: GET or POST.

GET method:

Data is sent in the URL itself. If you search for something on Google, look at the URL — you will see your search term right there in the address bar. That is GET.

GET is used for retrieving data — searches, filters, page numbers. It is not suitable for sensitive data like passwords because the data is visible in the URL.

POST method:

Data is sent in the body of the HTTP request — invisible in the URL. POST is used for sending sensitive or large data — login forms, registration forms, file uploads, anything where you do not want the data exposed in the URL.

Real world analogy: GET is like writing your message on a postcard — anyone who handles it can read it. POST is like putting your message inside a sealed envelope — only the recipient opens it.


Your First PHP Form

Let us build one. Create a new file called form.php in your phplearning folder and write this:


    <!DOCTYPE html>
    <html>

    <head>
        <title>My First PHP Form</title>
    </head>

    <body>

        <h2>Say Hello</h2>

        <form method="POST" action="form.php">
            <label>Your Name:</label>
            <input type="text" name="username" />
            <br><br>
            <label>Your Age:</label>
            <input type="number" name="age" />
            <br><br>
            <button type="submit">Submit</button>
        </form>

        <?php

        if ($_SERVER["REQUEST_METHOD"] == "POST") {
            $name = $_POST["username"];
            $age  = $_POST["age"];

            echo "<h3>Hello, $name! You are $age years old.</h3>";
        }

        ?>

    </body>

    </html>

Visit http://localhost:8080/phplearning/form.php

Type your name and age, click Submit. The page reloads and displays your personalized message below the form.

Let us understand every part of this.


Breaking Down the Form

method="POST"

This tells the browser to send form data using the POST method — hidden in the request body, not in the URL.

action="form.php"

This tells the browser where to send the data. We are sending it back to the same file — form.php processes both displaying the form and handling the submitted data. This is called a self-processing form.

name="username"

Every input field must have a name attribute. This is the key PHP uses to find that field's value on the server. Whatever you write in name="username" becomes the key in the $_POST array.


The $_POST Superglobal

$_POST is a special built-in PHP array that automatically holds all data submitted via a POST form.

When the user submits the form, PHP automatically populates $_POST with key-value pairs where the key is the input's name attribute and the value is whatever the user typed.


    <?php

    // After form submission, $_POST looks like this:
    // $_POST["username"] = "Gagan"
    // $_POST["age"] = "22"

    $name = $_POST["username"];
    $age  = $_POST["age"];

Similarly, $_GET holds data sent via GET method — from URL parameters.


$_SERVER["REQUEST_METHOD"] — Why We Check This


    <?php

    if ($_SERVER["REQUEST_METHOD"] == "POST") {

    }

When someone first visits the page, they are making a GET request — just loading the page. No form has been submitted yet. $_POST is empty.

When they submit the form, the request method becomes POST. The if check makes sure we only try to read $_POST values after actual form submission. Without this check, PHP would throw errors trying to read $_POST["username"] when it does not exist yet.

$_SERVER is another superglobal — it contains information about the current HTTP request, server details, and the current file path.


Basic Input Validation

Never trust raw user input. Ever. This is one of the most important rules in web development.

Users can accidentally leave fields blank. Users can intentionally send malicious data. Always validate and clean input before using it.


    <?php

    if ($_SERVER["REQUEST_METHOD"] == "POST") {

        $name = trim($_POST["username"]);
        $age  = trim($_POST["age"]);

        if (empty($name)) {
            echo "<p style='color:red;'>Name is required.</p>";
        } elseif (empty($age)) {
            echo "<p style='color:red;'>Age is required.</p>";
        } elseif (!is_numeric($age) || $age < 1 || $age > 120) {
            echo "<p style='color:red;'>Please enter a valid age.</p>";
        } else {
            echo "<h3>Hello, $name! You are $age years old.</h3>";
        }
    }

trim() removes accidental whitespace from both ends of the input.

empty() returns true if the value is an empty string, null, or zero.

is_numeric() checks whether a value is a valid number.

This is basic validation — but it already catches the most common user errors.


The $_GET Superglobal — URL Parameters

GET data comes from the URL. You have seen this before on every website — URLs like:

example.com/products?category=laptops&page=2

Everything after the ? is called the query string. PHP reads it automatically into $_GET.

Create a new file called search.php:


    <!DOCTYPE html>
    <html>

    <head>
        <title>Search</title>
    </head>

    <body>

        <h2>Product Search</h2>

        <form method="GET" action="search.php">
            <input type="text" name="query" placeholder="Search products..." />
            <button type="submit">Search</button>
        </form>

        <?php

        if (isset($_GET["query"])) {
            $search = trim($_GET["query"]);

            if (!empty($search)) {
                echo "<p>You searched for: <strong>$search</strong></p>";
                echo "<p>Showing results for '$search'...</p>";
            } else {
                echo "<p>Please enter a search term.</p>";
            }
        }

        ?>

    </body>

    </html>

Visit http://localhost:8080/phplearning/search.php

Type something and search. Notice the URL changes to search.php?query=yourterm — the search term is right there in the URL. This is exactly how Google, Amazon, and every search feature works.

isset() checks if a variable or array key exists at all. We use it instead of empty() here because $_GET["query"] does not exist until the form is submitted — and accessing a non-existent array key throws a warning in PHP.


A Complete Real World Example — Contact Form

Let us build a complete contact form with proper validation and a success message.

Create contact.php in your phplearning folder:


    <!DOCTYPE html>
    <html>

    <head>
        <title>Contact Us</title>
    </head>

    <body>

        <h2>Contact Us</h2>

        <?php

        $errors  = [];
        $success = false;

        if ($_SERVER["REQUEST_METHOD"] == "POST") {

            $name    = trim($_POST["name"] ?? "");
            $email   = trim($_POST["email"] ?? "");
            $message = trim($_POST["message"] ?? "");

            if (empty($name)) {
                $errors[] = "Name is required.";
            }

            if (empty($email)) {
                $errors[] = "Email is required.";
            } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                $errors[] = "Please enter a valid email address.";
            }

            if (empty($message)) {
                $errors[] = "Message is required.";
            } elseif (strlen($message) < 20) {
                $errors[] = "Message must be at least 20 characters long.";
            }

            if (empty($errors)) {
                $success = true;
            }
        }

        if (!empty($errors)) {
            echo "<div style='background:#fff0f0; border:1px solid #f00; padding:12px; border-radius:6px; margin-bottom:16px;'>";
            echo "<strong>Please fix the following errors:</strong><ul>";
            foreach ($errors as $error) {
                echo "<li>$error</li>";
            }
            echo "</ul></div>";
        }

        if ($success) {
            echo "<div style='background:#f0fff4; border:1px solid #0a0; padding:16px; border-radius:6px;'>";
            echo "<h3>Thank you, $name!</h3>";
            echo "<p>Your message has been received. We will get back to you at $email shortly.</p>";
            echo "</div>";
        } else {

        ?>

            <form method="POST" action="contact.php">

                <label>Full Name *</label><br>
                <input
                    type="text"
                    name="name"
                    value="<?php echo htmlspecialchars($_POST['name'] ?? ''); ?>"
                    style="width:100%; padding:8px; margin:6px 0 14px;" /><br>

                <label>Email Address *</label><br>
                <input
                    type="email"
                    name="email"
                    value="<?php echo htmlspecialchars($_POST['email'] ?? ''); ?>"
                    style="width:100%; padding:8px; margin:6px 0 14px;" /><br>

                <label>Message * (minimum 20 characters)</label><br>
                <textarea name="message" rows="5" style="width:100%; padding:8px; margin:6px 0 14px;">
                    <?php echo htmlspecialchars($_POST['message'] ?? ''); ?>
                </textarea><br>

                <button
                    type="submit"
                    style="background:#6366f1; color:#fff; padding:10px 24px; border:none; border-radius:6px; font-size:15px; cursor:pointer;">
                    Send Message
                </button>

            </form>

        <?php } ?>

    </body>

    </html>

Visit http://localhost:8080/phplearning/contact.php


Try submitting with empty fields. Try an invalid email. Try a message that is too short. Then fill everything correctly and submit. Each scenario shows a different response.

Let us highlight two important things from this example.

filter_var with FILTER_VALIDATE_EMAIL:

PHP has a built-in function for validating email addresses. filter_var($email, FILTER_VALIDATE_EMAIL) returns the email if it is valid, or false if it is not. Never write your own email validation regex — use this built-in filter instead.

htmlspecialchars():

When we put user input back into the HTML form (to preserve what they typed after a failed submission), we wrap it in htmlspecialchars(). This converts special characters like < and > into safe HTML entities so that malicious users cannot inject HTML or JavaScript into your page. This is called XSS prevention — Cross-Site Scripting. Always use htmlspecialchars() when outputting user data back into HTML.

The ?? operator:

$_POST['name'] ?? '' means: if $_POST['name'] exists, use it — otherwise use an empty string. This prevents PHP warnings when the form has not been submitted yet and $_POST is empty.


What Did We Learn in This Post?

HTML forms send user data to PHP via GET or POST methods. GET sends data in the URL. POST sends data in the request body — suitable for sensitive information.

$_POST holds all data submitted via POST forms. $_GET holds data from URL query strings. Both are superglobals — automatically available everywhere in PHP.

$_SERVER["REQUEST_METHOD"] tells you whether the current request is GET or POST — use it to check if a form was actually submitted before trying to read $_POST.

isset() checks if a variable or array key exists. empty() checks if a value is empty. Always validate before using user input.

filter_var() with FILTER_VALIDATE_EMAIL is the proper way to validate email addresses in PHP.

htmlspecialchars() prevents XSS attacks by converting special characters before outputting user data back into HTML.

The ?? null coalescing operator safely handles missing array keys without throwing PHP warnings.


What is Coming in Episode 10?

Now that we can receive and validate user input through forms, the next step is keeping that data alive across multiple pages.

Episode 10 covers PHP sessions and cookies — how PHP remembers who a user is from one page to the next. This is how login systems work. This is how shopping carts persist. This is how "Welcome back, Gagan" appears on every page after you log in.

See you in the next one.


Next Episode: Sessions and Cookies — How PHP Remembers Users

This is Episode 09 of the PHP and Laravel — Zero to Hero series.

Interesting Facts About PHP You Must Know

PHP powers a massive chunk of the internet and has been doing so for over three decades. Most developers use it daily without knowing its backstory, its quirks, or the surprising places it shows up. Here are the facts worth knowing.


1. PHP Was Never Meant to Be a Programming Language

PHP was created in 1994 by Rasmus Lerdorf — not to build a language, but to track visitors on his personal homepage. He wrote a handful of CGI scripts in C and called the collection "Personal Home Page Tools". That's where the name PHP came from.

It was never designed. It grew organically out of necessity. Lerdorf himself has said publicly:

"I don't know how to stop it, there was never any intent to write a programming language."

What started as a visitor counter eventually became the language running a third of the internet.


2. PHP No Longer Stands for What It Used to

The original meaning was Personal Home Page. As the language grew into something far beyond personal pages, the name was officially rebranded to a recursive acronym:

PHP: Hypertext Preprocessor

A recursive acronym means the abbreviation references itself in its own expansion — PHP stands for PHP Hypertext Preprocessor. It's a tradition in the programming world, borrowing from GNU (GNU's Not Unix).


3. WordPress, Facebook, and Wikipedia All Run on PHP

Some of the most visited websites in the world were built on PHP:

  • WordPress — powers over 43% of all websites on the internet, written entirely in PHP
  • Facebook — was built on PHP and still uses a heavily modified version today
  • Wikipedia — runs on MediaWiki, a PHP application
  • Slack — originally built with PHP on the backend

Facebook's usage grew so large that they built HipHop for PHP (HPHPc), a transpiler that converted PHP to C++ for performance. They later built HHVM (HipHop Virtual Machine) as a full PHP runtime alternative.


4. Facebook Built Their Own PHP Compiler

Standard PHP wasn't fast enough for Facebook's scale. So they built HHVM — a JIT-compiled runtime for PHP that dramatically improved execution speed. They also created Hack, a PHP-derived language with strict static typing, which Facebook's codebase runs on today.

The fact that a company at Facebook's scale invested that heavily in PHP infrastructure instead of rewriting everything is a testament to how deeply PHP was embedded in their systems.


5. PHP 6 Never Existed

PHP's version history goes:

PHP 5 → PHP 7 → PHP 8

PHP 6 was an ambitious project started around 2005 with a primary goal of native Unicode support. The project ran for years, faced enormous technical challenges with Unicode string handling, and was eventually abandoned in 2010.

So much of the PHP 6 work that was actually completed got backported into PHP 5.3 instead. The version number 6 was permanently skipped to avoid confusion with all the documentation, books, and articles that had already been written about the abandoned PHP 6 features.


6. PHP 7 Was a Massive Performance Leap

When PHP 7 released in December 2015, it wasn't just an incremental update. Benchmarks showed:

  • 2x faster than PHP 5.6 on most workloads
  • 50% lower memory consumption
  • Performance comparable to HHVM in many scenarios

This came from a complete internal rewrite of PHP's data structures and execution engine called phpng (PHP Next Generation). PHP 7 made a lot of "PHP is slow" arguments obsolete overnight.


7. PHP 8 Introduced a JIT Compiler

PHP 8.0 released in November 2020 and brought a Just-In-Time (JIT) compiler to PHP for the first time. JIT compiles code to native machine instructions at runtime instead of interpreting bytecode — the same concept that makes modern JavaScript engines fast.

PHP 8 also introduced:

  • Named argumentsarray_slice(array: $arr, offset: 1)
  • Match expressions — a stricter, cleaner switch
  • Nullsafe operator$user?->getAddress()?->getCity()
  • Union typesfunction foo(int|string $value)
  • Attributes — native annotation syntax replacing PHPDoc

PHP 8 was arguably the most significant modernization of the language since PHP 5.


8. PHP Has a Type System — It Just Doesn't Force You to Use It

PHP started as a loosely typed language where variables had no declared types. But since PHP 7, a full type system has been progressively added:

<?php

declare(strict_types=1);

function calculateTotal(int $quantity, float $price): float
{
    return $quantity * $price;
}

With declare(strict_types=1) at the top of a file, PHP enforces types strictly — passing the wrong type throws a TypeError. Without it, PHP coerces types silently.

PHP 8.x continues expanding the type system with intersection types, enum types, readonly properties, and never return types. Modern PHP code can be as strictly typed as TypeScript if you choose to write it that way.


9. The $ Variable Syntax Comes From Perl

PHP borrowed heavily from Perl in its early days, including the $ prefix for all variables. Perl was the dominant server-side scripting language when PHP was being built, and Rasmus Lerdorf was familiar with it.

$name = "Gagan";
$age = 25;
$isActive = true;

Every variable in PHP regardless of type uses $. It's one of the most immediately recognizable things about PHP syntax and it comes directly from Perl's influence.


10. PHP Has a Built-In Web Server

Since PHP 5.4, PHP ships with a built-in development web server — no Apache or Nginx needed for local development:

php -S localhost:8000

Point it at a directory and it serves files immediately. Laravel's php artisan serve command uses this exact built-in server under the hood.

It's not meant for production — it's single-threaded and handles one request at a time — but for local development and quick prototyping it's perfectly capable.


11. echo is Not a Function

This catches a lot of developers off guard. echo in PHP is a language construct, not a function:

echo "Hello";
echo("Hello");

Both work, but echo doesn't have a return value and can't be used as an expression. This is why you can't do:

$result = echo "Hello";

Same applies to print, isset, unset, list, array, and require — all language constructs, not functions, even though some of them look exactly like function calls.


12. PHP Arrays Are Actually Ordered Hash Maps

In most languages, arrays and hash maps (dictionaries/objects) are completely separate data structures. In PHP, there is only one data structure — the array — and it handles both:

$indexed = [1, 2, 3];

$associative = [
    'name' => 'Gagan',
    'role' => 'developer',
];

$mixed = [
    0 => 'first',
    'key' => 'value',
    1 => 'second',
];

Under the hood, every PHP array is an ordered hash map that maintains insertion order. This is different from JavaScript objects (which lost order in older specs) and Python dicts (which only guaranteed order from Python 3.7). PHP arrays have always maintained order, always.


13. Composer Changed PHP Development Completely

Before Composer launched in 2012, PHP had no standard dependency manager. Developers manually downloaded libraries, managed include paths, and dealt with version conflicts by hand. PEAR existed but was notoriously difficult to use.

Composer brought semantic versioning, a central package repository (Packagist), autoloading via PSR-4, and proper dependency resolution to PHP. It's the single biggest tooling improvement in PHP's history. Modern PHP development — Laravel, Symfony, everything — is completely built on top of Composer.


14. PHP Runs on 78% of Websites With a Known Server-Side Language

According to W3Techs data, PHP is used by 78.1% of all websites whose server-side language is known. The next closest is ASP.NET at around 6%. No other server-side language is even close in terms of deployment footprint.

This isn't because PHP is the best language by every metric — it's because it's been around long enough, is easy to host, and powers WordPress, which alone accounts for a huge portion of that number.


15. PHP's Mascot is an Elephant Named ElePHPant

Since 2007, PHP's official mascot has been the ElePHPant — a blue elephant with the PHP logo on its side. It was designed by Vincent Pontier.

The name is a portmanteau of elephant and PHP. Physical plush ElePHPant toys have been produced in multiple colors over the years and are considered collector's items in the PHP community. Different colors were produced for specific conferences, companies, and framework communities — Laravel has its own red version, Symfony has its own, and so on.


The Bottom Line

PHP has a strange history — born as a personal project, abandoned more than once, declared dead repeatedly, and yet still running the majority of the web three decades later. It's been through bad API design decisions, a cancelled major version, a complete internal rewrite, and a JIT revolution. Understanding where it came from and how it evolved makes you a better PHP developer — because a lot of the language's quirks make complete sense once you know the story behind them.

Setting Up and Configuring a Laravel Development Environment in VS Code

Before writing a single line of Laravel code, your machine needs a few things in place. This guide walks through everything from scratch — PHP, Composer, Laravel itself, and VS Code configuration — so your environment is clean, professional, and ready for real development.


What You Need Before Starting

Tool Purpose
PHP 8.2+ Laravel 11 requires PHP 8.2 minimum
Composer PHP's package manager — installs Laravel and dependencies
VS Code Your editor
XAMPP or standalone PHP Provides PHP and MySQL locally
Node.js + npm For frontend asset compilation (Vite)

Step 1 — PHP Setup

If you have XAMPP, PHP is already installed. Just make sure it's in your PATH as covered earlier. Verify in terminal:

php --version

Expected:

PHP 8.2.x (cli)

If your XAMPP ships with an older PHP version, download a newer standalone PHP from windows.php.net/download and point your PATH to that instead.


Step 2 — Install Composer

Composer is non-negotiable for Laravel. It manages every dependency the framework needs.

Windows

Download and run the installer from:

https://getcomposer.org/Composer-Setup.exe

The installer automatically adds Composer to your PATH.

macOS / Linux

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
sudo mv composer.phar /usr/local/bin/composer

Verify installation:

composer --version
Composer version 2.7.x

Step 3 — Install Laravel

Option A — Via Laravel Installer (Recommended)

composer global require laravel/installer

After installation, create a new project:

laravel new my-project

During setup, Laravel 11 will ask you a few questions:

Which starter kit would you like to install?
> No starter kit

Which testing framework do you prefer?
> Pest

Would you like to initialize a Git repository?
> Yes

For a clean backend-focused setup, choose no starter kit.

Option B — Via Composer Directly

composer create-project laravel/laravel my-project

Both approaches produce the same result. Option A is faster for repeated use.


Step 4 — Open Project in VS Code

cd my-project
code .

Your project structure will look like this:

my-project/
├── app/
│   ├── Http/
│   │   ├── Controllers/
│   │   └── Middleware/
│   └── Models/
├── bootstrap/
├── config/
├── database/
│   ├── migrations/
│   └── seeders/
├── public/
├── resources/
│   ├── views/
│   └── js/
├── routes/
│   ├── web.php
│   └── api.php
├── storage/
├── tests/
├── .env
├── artisan
├── composer.json
└── vite.config.js

Step 5 — Configure the .env File

The .env file controls your entire local environment — database, mail, cache, and more. Open it and configure the database section:

APP_NAME=MyProject
APP_ENV=local
APP_KEY=base64:...
APP_DEBUG=true
APP_URL=http://localhost:8000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_project
DB_USERNAME=root
DB_PASSWORD=

For XAMPP, the default MySQL username is root with no password. Create the database manually via phpMyAdmin or terminal:

mysql -u root -e "CREATE DATABASE my_project;"

Step 6 — Run Migrations

Once the database is created and .env is configured:

php artisan migrate
INFO  Running migrations.

  2014_10_12_000000_create_users_table ............. 12ms DONE
  2014_10_12_100000_create_password_reset_tokens_table .. 8ms DONE
  2019_08_19_000000_create_failed_jobs_table ........ 6ms DONE
  2024_01_01_000000_create_cache_table .............. 5ms DONE

Laravel's default migrations are now applied to your database.


Step 7 — Start the Development Server

php artisan serve
INFO  Server running on [http://127.0.0.1:8000].

Open your browser and visit http://127.0.0.1:8000. You should see the Laravel welcome page. Your server is running.


Step 8 — Essential VS Code Extensions

This is where your editor transforms from a basic text editor into a proper Laravel IDE.

Must-Have Extensions

Laravel Extension Pack — installs a curated bundle of Laravel-specific tools in one click:

Publisher: onecentlin
ID: onecentlin.laravel-extension-pack

Or install individually:

Extension Publisher Purpose
Laravel Blade Snippets Winnie Lin Blade syntax highlighting and snippets
Laravel Snippets Winnie Lin PHP/Laravel code snippets
Laravel Artisan Ryan Naddy Run artisan commands from VS Code command palette
Laravel Blade formatter Shuhei Hayashibara Auto-formats Blade files on save
PHP Intelephense Ben Massen PHP intellisense, go-to-definition, type inference
PHP DocBlocker Neil Brayfield Auto-generates PHPDoc blocks
DotENV mikestead Syntax highlighting for .env files
Prettier Prettier Formats JS, CSS, JSON files
GitLens GitKraken Enhanced Git history and blame

PHP Intelephense Setup

After installing Intelephense, disable VS Code's built-in PHP language features to avoid conflicts:

  1. Open Command Palette — Ctrl + Shift + P
  2. Search "Extensions: Show Built-in Extensions"
  3. Find PHP Language Features
  4. Click Disable

Keep PHP Language Basics enabled — it handles syntax coloring. Only disable PHP Language Features since Intelephense handles that better.


Step 9 — VS Code Workspace Settings

Create a .vscode/settings.json file inside your project root:

{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.tabSize": 4,
    "editor.insertSpaces": true,
    "[php]": {
        "editor.defaultFormatter": "bmewburn.vscode-intelephense-client",
        "editor.formatOnSave": true
    },
    "[blade]": {
        "editor.defaultFormatter": "shufo.vscode-blade-formatter",
        "editor.formatOnSave": true
    },
    "files.associations": {
        "*.blade.php": "blade"
    },
    "intelephense.environment.phpVersion": "8.2.0",
    "intelephense.files.exclude": [
        "**/vendor/**"
    ],
    "emmet.includeLanguages": {
        "blade": "html"
    }
}

This gives you auto-formatting on save for PHP, Blade, and JS files separately, correct file type associations, and Emmet support inside Blade templates.


Step 10 — Configure Xdebug (Optional but Recommended)

Debugging with dd() and var_dump() only gets you so far. Xdebug gives you real breakpoints inside VS Code.

Install Xdebug

For XAMPP on Windows, Xdebug is often already included. Check your php.ini:

php -m | grep xdebug

If not installed, download the correct .dll from xdebug.org/wizard — paste your phpinfo() output and it tells you exactly which file to download.

Configure php.ini

Add to the end of your php.ini (found at C:\xampp\php\php.ini):

[xdebug]
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_port=9003
xdebug.client_host=127.0.0.1

VS Code Launch Configuration

Create .vscode/launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "pathMappings": {
                "${workspaceFolder}": "${workspaceFolder}"
            }
        }
    ]
}

Install the PHP Debug extension by Xdebug, press F5 to start listening, then set breakpoints by clicking the gutter next to any line number. When a request hits that code, VS Code pauses execution and lets you inspect every variable in scope.


Step 11 — Frontend Assets With Vite

Laravel 11 uses Vite for frontend asset bundling. Install Node dependencies first:

npm install

Run Vite in development mode (keep this running alongside php artisan serve):

npm run dev

For production builds:

npm run build

You'll typically have two terminals open during development — one running php artisan serve, one running npm run dev.


Daily Development Workflow

Once everything is set up, your typical workflow looks like this:

# Terminal 1 — PHP server
php artisan serve

# Terminal 2 — Vite assets
npm run dev

Common artisan commands you'll use constantly:

php artisan make:model Post -mcr
php artisan make:controller PostController
php artisan make:migration create_posts_table
php artisan migrate
php artisan migrate:rollback
php artisan route:list
php artisan cache:clear
php artisan config:clear
php artisan tinker

VS Code with the Laravel Artisan extension lets you run these from the command palette (Ctrl + Shift + P → "Artisan") without leaving the editor.


The Bottom Line

A proper Laravel setup in VS Code comes down to three layers — the right PHP version and Composer at the system level, a correctly configured .env and database at the project level, and the right extensions and workspace settings at the editor level. Get all three right once and your environment stays out of your way, letting you focus entirely on building.

PHP & Laravel — Zero to Hero Episode 07: Arrays — Storing and Managing Collections of Data

What Are We Doing in This Post?

So far every variable we created stored exactly one value. One name, one price, one score.

But real applications deal with collections. A list of products. A set of user details. A group of orders. Storing each one in a separate variable would be chaos.

Arrays solve this. An array is a single variable that holds multiple values. This is one of the most important data structures in PHP and you will use arrays in literally every project you ever build.


What is an Array?

Real world analogy: Think of an array like a train. A train has multiple compartments and each compartment holds passengers. The train itself has one name, but it carries many things inside it. A variable holds the train's name. An array is the train — one name, many compartments.


    <?php

    $colors = ["Red", "Green", "Blue", "Yellow"];

    print_r($colors);

Output:

Array ( [0] => Red [1] => Green [2] => Blue [3] => Yellow )

print_r is a built-in PHP function that displays the full contents of an array in a readable format. We use it constantly while working with arrays.

Notice the numbers 0, 1, 2, 3 next to each value. These are called indexes. PHP automatically assigns a numeric index to each item starting from 0, not 1. This is called zero-based indexing and it is standard across almost all programming languages.


Accessing Array Items

To access a specific item from an array, you use its index inside square brackets.


    <?php

    $colors = ["Red", "Green", "Blue", "Yellow"];

    echo $colors[0];
    echo "<br>";
    echo $colors[2];
    echo "<br>";
    echo $colors[3];

Output:

Red
Blue
Yellow

$colors[0] gives you the first item. $colors[2] gives you the third item. Remember — counting starts at zero.


Modifying and Adding Items


    <?php

    $colors = ["Red", "Green", "Blue"];

    $colors[1] = "Purple";

    $colors[] = "Orange";

    print_r($colors);

Output:

Array ( [0] => Red [1] => Purple [2] => Blue [3] => Orange )

Assigning to an existing index overwrites that value. Green became Purple at index 1.

Using empty square brackets $colors[] = adds a new item at the end of the array automatically. PHP figures out the next available index on its own.


Associative Arrays — Arrays With Named Keys

An indexed array uses numbers as keys. An associative array lets you define your own meaningful key names.

Real world analogy: An indexed array is like a numbered locker in a gym — locker 1, locker 2, locker 3. An associative array is like a hotel room with a name tag — "Presidential Suite", "Deluxe Room", "Standard Room." The name tells you exactly what is inside.


    <?php

    $user = ["name" => "Gagan", "email" => "gagan@gmail.com", "age" => 22, "city" => "Delhi", "is_premium" => true];

    echo $user["name"];
    echo "<br>";
    echo $user["email"];
    echo "<br>";
    echo $user["age"];

Output:

Gagan
gagan@gmail.com
22

The => symbol maps a key to a value. To access a value you use its key name inside square brackets. This is far more readable than remembering that index 0 is name and index 3 is city.

Associative arrays are how you will represent a single record from a database in PHP. When Laravel fetches a user from the database, it comes back as an associative array or object with named fields exactly like this.


Multidimensional Arrays — Arrays Inside Arrays

A multidimensional array is an array where each item is itself an array. This lets you represent structured data like a table.

Real world analogy: Think of a spreadsheet. The spreadsheet is the outer array. Each row is an inner array. Each cell in a row is a value.


    <?php

    $students = [
        ["name" => "Gagan", "score" => 88, "grade" => "B"],
        ["name" => "Rahul", "score" => 95, "grade" => "A"],
        ["name" => "Priya", "score" => 72, "grade" => "C"],
        ["name" => "Anjali", "score" => 60, "grade" => "C"],
    ];

    foreach ($students as $student) {
        echo $student["name"] . " — Score: " . $student["score"] . " — Grade: " . $student["grade"];
        echo "<br>";
    }

Output:

Gagan — Score: 88 — Grade: B
Rahul — Score: 95 — Grade: A
Priya — Score: 72 — Grade: C
Anjali — Score: 60 — Grade: C

This structure is almost identical to what you get when you fetch multiple rows from a database. Each row is an associative array. All rows together form a multidimensional array. Learning to think in this structure now will make database work in Laravel feel completely natural.

To access a specific cell directly:


    <?php

    echo $students[1]["name"];

Output: Rahul

$students[1] gets the second student (index 1). ["name"] gets the name field from that student's array.


Built-in Array Functions

PHP gives you a rich set of built-in functions for working with arrays. Here are the most important ones.

count — how many items are in the array:


    <?php

    $fruits = ["Apple", "Banana", "Mango", "Orange"];
    echo count($fruits);

Output: 4

Real world use: displaying "Showing 4 of 20 products" on a listing page.

array_push and array_pop — add and remove from the end:


    <?php

    $cart = ["Keyboard", "Mouse"];

    array_push($cart, "Monitor");
    print_r($cart);
    echo "<br>";

    array_pop($cart);
    print_r($cart);

Output:

Array ( [0] => Keyboard [1] => Mouse [2] => Monitor )
Array ( [0] => Keyboard [1] => Mouse )

array_push adds one or more items to the end. array_pop removes the last item. Real world use: managing a shopping cart — add items, remove the last added item.

array_shift and array_unshift — remove and add from the beginning:


    <?php

    $queue = ["Person A", "Person B", "Person C"];

    array_shift($queue);
    print_r($queue);
    echo "<br>";

    array_unshift($queue, "Person Z");
    print_r($queue);

Output:

Array ( [0] => Person B [1] => Person C )
Array ( [0] => Person Z [1] => Person B [2] => Person C )

array_shift removes the first item. array_unshift adds a new item to the beginning. Real world use: managing a queue system where the first person in line gets served first.

in_array — check if a value exists:


    <?php

    $allowed_roles = ["admin", "editor", "moderator"];
    $user_role = "editor";

    if (in_array($user_role, $allowed_roles)) {
        echo "Access granted.";
    } else {
        echo "Access denied.";
    }

Output: Access granted.

Real world use: permission and role checking systems. Does this user have a role that allows them to access this page?

array_search — find the key of a value:


    <?php

    $cities = ["Mumbai", "Delhi", "Bangalore", "Chennai"];
    $key = array_search("Bangalore", $cities);
    echo $key;

Output: 2

Returns the index of the found value. Returns false if not found.

sort and rsort — sort indexed arrays:


    <?php

    $scores = [45, 92, 67, 23, 88];

    sort($scores);
    print_r($scores);
    echo "<br>";

    rsort($scores);
    print_r($scores);

Output:

Array ( [0] => 23 [1] => 45 [2] => 67 [3] => 88 [4] => 92 )
Array ( [0] => 92 [1] => 88 [2] => 67 [3] => 45 [4] => 23 )

sort sorts ascending. rsort sorts descending.

ksort and asort — sort associative arrays:


    <?php

    $prices = ["Monitor" => 15000, "Keyboard" => 2500, "Mouse" => 800];

    asort($prices);
    print_r($prices);

Output:

Array ( [Mouse] => 800 [Keyboard] => 2500 [Monitor] => 15000 )

asort sorts by value while keeping key-value pairs intact. ksort sorts by key alphabetically.

array_merge — combine two arrays:


    <?php

    $electronics = ["Laptop", "Phone"];
    $accessories = ["Case", "Charger", "Cable"];

    $all_products = array_merge($electronics, $accessories);
    print_r($all_products);

Output:

Array ( [0] => Laptop [1] => Phone [2] => Case [3] => Charger [4] => Cable )

array_unique — remove duplicate values:


    <?php

    $tags = ["php", "laravel", "php", "web", "laravel", "backend"];
    $unique_tags = array_unique($tags);
    print_r($unique_tags);

Output:

Array ( [0] => php [1] => laravel [3] => web [5] => backend )

Duplicates removed. Real world use: cleaning up tag lists or category selections where users may submit duplicates.

array_slice — extract a portion of an array:


    <?php

    $products = ["A", "B", "C", "D", "E", "F", "G"];
    $page_one = array_slice($products, 0, 3);
    $page_two = array_slice($products, 3, 3);

    print_r($page_one);
    echo "<br>";
    print_r($page_two);

Output:

Array ( [0] => A [1] => B [2] => C )
Array ( [0] => D [1] => E [2] => F )

Real world use: pagination. Show 3 items per page. Page one gets items 0 to 2. Page two gets items 3 to 5.


A Real World Example — Shopping Cart System

Let us build a simple shopping cart using everything from this episode.

Create cart.php in your phplearning folder:


    <!DOCTYPE html>
    <html>

    <head>
        <title>Shopping Cart</title>
    </head>

    <body>

        <?php
        $cart = [
            ["name" => "Wireless Mouse",      "price" => 599,  "qty" => 2],
            ["name" => "Mechanical Keyboard", "price" => 2499, "qty" => 1],
            ["name" => "USB Hub",             "price" => 899,  "qty" => 3],
        ];

        $total = 0;

        echo "<h2>Your Cart</h2>";
        echo "<table border='1' cellpadding='8'>";
        echo "<tr><th>Product</th><th>Price</th><th>Qty</th><th>Subtotal</th></tr>";

        foreach ($cart as $item) {
            $subtotal = $item["price"] * $item["qty"];
            $total += $subtotal;

            echo "<tr>";
            echo "<td>" . htmlspecialchars($item["name"]) . "</td>";
            echo "<td>Rs. " . $item["price"] . "</td>";
            echo "<td>" . $item["qty"] . "</td>";
            echo "<td>Rs. " . $subtotal . "</td>";
            echo "</tr>";
        }

        echo "</table>";
        echo "<h3>Total Items: " . count($cart) . "</h3>";
        echo "<h3>Grand Total: Rs. " . $total . "</h3>";

        $discount = ($total > 3000) ? "10% discount applied!" : "No discount available.";
        echo "<p>" . $discount . "</p>";
        ?>

    </body>

    </html>

Visit http://localhost:8080/phplearning/cart.php

You will see a proper cart table with product names, prices, quantities, subtotals, a grand total, and a discount message that appears automatically when the total crosses Rs. 3000.

This combines multidimensional arrays, foreach loops, arithmetic operators, the ternary operator, and string output — everything from the last four episodes working together as one complete feature.


What Did We Learn in This Post?

Arrays store multiple values in a single variable. Indexed arrays use numeric keys starting from 0. Associative arrays use custom named keys defined with =>.

Multidimensional arrays store arrays inside arrays — this mirrors how database rows look when fetched in PHP and Laravel.

Essential array functions: count, array_push, array_pop, array_shift, array_unshift, in_array, array_search, sort, rsort, asort, array_merge, array_unique, and array_slice.

Arrays, loops, and conditionals work together as the foundation of almost every dynamic feature in a real web application.


What is Coming in Episode 08?

Next we cover functions — how to write reusable blocks of code that you can call anywhere in your application.

Functions are how you stop repeating yourself. Instead of writing the same logic in ten different places, you write it once as a function and call it wherever you need it. Episode 08 covers defining functions, parameters, return values, default arguments, and variable scope.

See you in the next one.


Next Episode: Functions — Writing Reusable Code in PHP

This is Episode 07 of the PHP and Laravel — Zero to Hero series.

PHP & Laravel — Zero to Hero Episode 14: Interfaces, Abstract Classes, and Namespaces — The Last Core PHP Concepts Before Laravel

What Are We Doing in This Post? This is the final Core PHP episode. After this, we start Laravel. In Episode 13 we learned classes, objec...